1149 lines
51 KiB
C
1149 lines
51 KiB
C
// Init service manager. The kernel bootstraps only init; init starts and supervises user-mode system services.
|
|
|
|
#include <stdint.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"
|
|
|
|
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;
|
|
|
|
typedef struct INIT_RESOURCE_SERVICE {
|
|
INIT_SERVICE Service;
|
|
SYS_HANDLE AdminListener;
|
|
SYS_HANDLE SessionControlListener;
|
|
} INIT_RESOURCE_SERVICE;
|
|
|
|
static INIT_SERVICE_CONFIG gServiceConfig;
|
|
|
|
// 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 least-privileged security context required by RSSRV.
|
|
static KSTATUS derive_resource_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 = SYS_PRIV_SYSTEM_SERVICE_REGISTER;
|
|
request.DelegablePrivileges = 0;
|
|
request.ServiceClaims = SYS_SERVICE_CLAIM_RESOURCE;
|
|
request.DelegableServiceClaims = 0;
|
|
request.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
|
|
request.ContextHandleOut = contextOut;
|
|
return SysDeriveSecurityContext(&request);
|
|
}
|
|
|
|
// 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.ServiceClaims = 0;
|
|
request.DelegableServiceClaims = 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;
|
|
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,
|
|
const NTL_PROCESS_STARTUP_CAPABILITY* startupCapabilities,
|
|
uint32_t startupCapabilityCount,
|
|
INIT_SERVICE* service) {
|
|
if (!service || !service->Descriptor || service->Descriptor->ImagePath[0] == '\0' ||
|
|
startupCapabilityCount > SYS_STARTUP_CAPABILITY_MAX - 1u ||
|
|
(startupCapabilityCount && !startupCapabilities))
|
|
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));
|
|
for (uint32_t i = 0; i < startupCapabilityCount; ++i)
|
|
startup[i] = startupCapabilities[i];
|
|
startup[startupCapabilityCount].Tag = SYS_STARTUP_CAP_SERVICE_CONTROL;
|
|
startup[startupCapabilityCount].SourceHandle = controlListener;
|
|
startup[startupCapabilityCount].DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_ACCEPT;
|
|
startup[startupCapabilityCount].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
|
|
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 = startupCapabilityCount + 1u;
|
|
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) {
|
|
if (!service || !service->Descriptor || !exportCountOut ||
|
|
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) &&
|
|
message.Size == sizeof(message) &&
|
|
message.Version == NTL_SERVICE_CONTROL_VERSION &&
|
|
message.Operation == NTL_SERVICE_CONTROL_READY &&
|
|
message.ExportCount == info.HandleCount &&
|
|
message.ExportCount <= exportCapacity &&
|
|
message.Reserved[0] == 0 && message.Reserved[1] == 0 && message.Reserved[2] == 0;
|
|
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;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Waits for RSSRV readiness and receives init-only namespace and DWM-session bootstrap exports.
|
|
static KSTATUS wait_resource_service_ready(INIT_SERVICE* service,
|
|
SYS_HANDLE* adminListenerOut,
|
|
SYS_HANDLE* sessionControlListenerOut) {
|
|
if (!adminListenerOut || !sessionControlListenerOut) return NTLSTATUS_INVALID_PARAMETER;
|
|
*adminListenerOut = SYS_HANDLE_INVALID;
|
|
*sessionControlListenerOut = SYS_HANDLE_INVALID;
|
|
|
|
SYS_HANDLE exports[2] = { SYS_HANDLE_INVALID, SYS_HANDLE_INVALID };
|
|
uint32_t exportCount = 0;
|
|
KSTATUS status = wait_service_ready(service, exports, 2u, &exportCount);
|
|
if (!KSUCCESS(status)) return status;
|
|
if (exportCount != 2u || exports[0] == SYS_HANDLE_INVALID || exports[1] == SYS_HANDLE_INVALID) {
|
|
for (uint32_t i = 0; i < 2u; ++i)
|
|
if (exports[i] != SYS_HANDLE_INVALID) (void)SysCloseHandle(exports[i]);
|
|
return NTLSTATUS_FAILURE;
|
|
}
|
|
|
|
*adminListenerOut = exports[0];
|
|
*sessionControlListenerOut = exports[1];
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Verifies namespace structure, administration, symbolic links, enumeration, and public capability lookup.
|
|
static KSTATUS verify_resource_service(SYS_HANDLE adminListener, SYS_HANDLE namespaceVolume) {
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE publicSession = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE volume = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE displayDevice = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE consoleAlias = SYS_HANDLE_INVALID;
|
|
uint32_t initDirectoryCreated = 0;
|
|
uint32_t aliasCreated = 0;
|
|
uint32_t probeBound = 0;
|
|
NTL_RESOURCE_ENUM_CURSOR cursor;
|
|
uint32_t sawAlias = 0;
|
|
uint32_t sawProbe = 0;
|
|
memset(&cursor, 0, sizeof(cursor));
|
|
KSTATUS status = NtlResourceConnectListener(adminListener, &admin);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
uint32_t kind = NTL_RESOURCE_KIND_INVALID;
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/SystemRoot"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_SYMBOLIC_LINK) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/Device/Display0"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_DEVICE) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceLookup(admin, SYS_SV_LITERAL("/Device/Display0"),
|
|
SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE, &displayDevice);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
SYS_DISPLAY_INFO displayInfo;
|
|
memset(&displayInfo, 0, sizeof(displayInfo));
|
|
status = SysQueryDisplayManagerInfo(displayDevice, &displayInfo);
|
|
(void)SysCloseHandle(displayDevice);
|
|
displayDevice = SYS_HANDLE_INVALID;
|
|
if (!KSUCCESS(status) || displayInfo.Mode.Width == 0 || displayInfo.Mode.Height == 0) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/Sessions/0"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_NAMESPACE_DIRECTORY) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/Sessions/0/Desktop"), &kind);
|
|
if (status != NTLSTATUS_NOT_FOUND) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/Sessions/0/Console"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_CAPABILITY) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL("/Console"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_SYMBOLIC_LINK) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
status = NtlResourceLookup(admin, SYS_SV_LITERAL("/Console"),
|
|
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT, &consoleAlias);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
(void)SysCloseHandle(consoleAlias);
|
|
consoleAlias = SYS_HANDLE_INVALID;
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL(NTL_INPUT_SERVICE_PATH), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_CAPABILITY) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
SYS_HANDLE inputSession = SYS_HANDLE_INVALID;
|
|
status = NtlInputConnect(&inputSession);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
NTL_INPUT_STATE inputState;
|
|
status = NtlInputQueryState(inputSession, &inputState);
|
|
(void)NtlInputClose(inputSession);
|
|
if (!KSUCCESS(status) || inputState.Size != sizeof(inputState) ||
|
|
inputState.Flags != NTL_INPUT_STATE_FLAG_NONE) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtlResourceQuery(admin, SYS_SV_LITERAL(NTL_CONSOLE_SERVICE_PATH), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_CAPABILITY) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
SYS_HANDLE consoleSession = SYS_HANDLE_INVALID;
|
|
status = NtlConsoleConnect(&consoleSession);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
NTL_CONSOLE_INFO consoleInfo;
|
|
memset(&consoleInfo, 0, sizeof(consoleInfo));
|
|
status = NtlConsoleQueryInfo(consoleSession, &consoleInfo);
|
|
(void)NtlConsoleClose(consoleSession);
|
|
if (!KSUCCESS(status) || consoleInfo.Size != sizeof(consoleInfo) ||
|
|
consoleInfo.Flags != NTL_CONSOLE_FLAG_NONE || consoleInfo.Columns == 0 ||
|
|
consoleInfo.Rows == 0 || consoleInfo.PixelWidth == 0 || consoleInfo.PixelHeight == 0) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
|
|
// Exercise explicit parent creation rather than allowing bind to manufacture namespace directories.
|
|
status = NtlResourceCreateDirectory(admin, SYS_SV_LITERAL("/Init"));
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
initDirectoryCreated = 1;
|
|
|
|
// Exercise a symbolic-link chain: /Init/SystemRoot -> /SystemRoot -> /Device/HarddiskVolumeN.
|
|
status = NtlResourceCreateSymbolicLink(admin, SYS_SV_LITERAL("/Init/SystemRoot"),
|
|
SYS_SV_LITERAL("/SystemRoot"));
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
aliasCreated = 1;
|
|
|
|
status = NtlResourceBind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"),
|
|
NTL_RESOURCE_KIND_VOLUME, namespaceVolume,
|
|
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN,
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
probeBound = 1;
|
|
|
|
status = NtlResourceConnect(&publicSession);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
|
|
status = NtlResourceLookup(publicSession, SYS_SV_LITERAL("/SystemRoot"),
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &volume);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
(void)SysCloseHandle(volume);
|
|
volume = SYS_HANDLE_INVALID;
|
|
|
|
status = NtlResourceLookup(publicSession, SYS_SV_LITERAL("/Init/SystemRoot"),
|
|
SYS_RIGHT_QUERY | SYS_VOLUME_RIGHT_OPEN, &volume);
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
(void)SysCloseHandle(volume);
|
|
volume = SYS_HANDLE_INVALID;
|
|
|
|
kind = NTL_RESOURCE_KIND_INVALID;
|
|
status = NtlResourceQuery(publicSession, SYS_SV_LITERAL("/Init/BootstrapProbe"), &kind);
|
|
if (!KSUCCESS(status) || kind != NTL_RESOURCE_KIND_VOLUME) {
|
|
status = KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
|
|
goto cleanup;
|
|
}
|
|
|
|
for (;;) {
|
|
NTL_RESOURCE_ENUM_ENTRY entry;
|
|
status = NtlResourceEnumerate(publicSession, SYS_SV_LITERAL("/Init"), &cursor, &entry);
|
|
if (status == NTLSTATUS_NO_MORE_FILES) {
|
|
status = NTLSTATUS_SUCCESS;
|
|
break;
|
|
}
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
if (strcmp(entry.Name, "SystemRoot") == 0 && entry.Kind == NTL_RESOURCE_KIND_SYMBOLIC_LINK)
|
|
sawAlias = 1;
|
|
if (strcmp(entry.Name, "BootstrapProbe") == 0 && entry.Kind == NTL_RESOURCE_KIND_VOLUME)
|
|
sawProbe = 1;
|
|
}
|
|
if (!sawAlias || !sawProbe) {
|
|
status = NTLSTATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
status = NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
if (!KSUCCESS(status)) goto cleanup;
|
|
probeBound = 0;
|
|
kind = NTL_RESOURCE_KIND_INVALID;
|
|
if (NtlResourceQuery(publicSession, SYS_SV_LITERAL("/Init/BootstrapProbe"), &kind) != NTLSTATUS_NOT_FOUND) {
|
|
status = NTLSTATUS_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if (consoleAlias != SYS_HANDLE_INVALID) (void)SysCloseHandle(consoleAlias);
|
|
if (displayDevice != SYS_HANDLE_INVALID) (void)SysCloseHandle(displayDevice);
|
|
if (volume != SYS_HANDLE_INVALID) (void)SysCloseHandle(volume);
|
|
if (publicSession != SYS_HANDLE_INVALID) (void)NtlResourceClose(publicSession);
|
|
if (probeBound) (void)NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/BootstrapProbe"));
|
|
if (aliasCreated) {
|
|
KSTATUS cleanupStatus = NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init/SystemRoot"));
|
|
if (KSUCCESS(status) && !KSUCCESS(cleanupStatus)) status = cleanupStatus;
|
|
}
|
|
if (initDirectoryCreated) {
|
|
KSTATUS cleanupStatus = NtlResourceUnbind(admin, SYS_SV_LITERAL("/Init"));
|
|
if (KSUCCESS(status) && !KSUCCESS(cleanupStatus)) status = cleanupStatus;
|
|
}
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
// Tears down retained RSSRV-specific and generic handles without sending a stop message.
|
|
static void close_resource_service(INIT_RESOURCE_SERVICE* resourceService) {
|
|
if (!resourceService) return;
|
|
if (resourceService->AdminListener != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(resourceService->AdminListener);
|
|
if (resourceService->SessionControlListener != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(resourceService->SessionControlListener);
|
|
resourceService->AdminListener = SYS_HANDLE_INVALID;
|
|
resourceService->SessionControlListener = SYS_HANDLE_INVALID;
|
|
close_service_handles(&resourceService->Service);
|
|
}
|
|
|
|
// Initializes the RSSRV-specific extension of one managed-service runtime.
|
|
static void initialize_resource_service(INIT_RESOURCE_SERVICE* resourceService,
|
|
const INIT_SERVICE_DESCRIPTOR* descriptor) {
|
|
memset(resourceService, 0, sizeof(*resourceService));
|
|
initialize_service(&resourceService->Service, descriptor);
|
|
resourceService->AdminListener = SYS_HANDLE_INVALID;
|
|
resourceService->SessionControlListener = SYS_HANDLE_INVALID;
|
|
}
|
|
|
|
// Starts one restricted RSSRV instance through the generic managed-service process path.
|
|
static KSTATUS start_resource_service(SYS_HANDLE systemVolume,
|
|
SYS_HANDLE namespaceVolume,
|
|
SYS_HANDLE volumeManager,
|
|
SYS_HANDLE inputManager,
|
|
SYS_HANDLE displayManager,
|
|
SYS_HANDLE resourceContext,
|
|
INIT_RESOURCE_SERVICE* resourceService) {
|
|
if (!resourceService || !resourceService->Service.Descriptor ||
|
|
resourceService->Service.Descriptor->Type != InitServiceTypeResource ||
|
|
resourceService->AdminListener != SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
NTL_PROCESS_STARTUP_CAPABILITY startup[4];
|
|
memset(startup, 0, sizeof(startup));
|
|
startup[0].Tag = SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE;
|
|
startup[0].SourceHandle = namespaceVolume;
|
|
startup[0].DesiredRights = SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER |
|
|
SYS_VOLUME_RIGHT_OPEN | SYS_VOLUME_RIGHT_MODIFY;
|
|
startup[0].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[1].Tag = SYS_STARTUP_CAP_VOLUME_MANAGER;
|
|
startup[1].SourceHandle = volumeManager;
|
|
startup[1].DesiredRights = SYS_RIGHT_QUERY |
|
|
SYS_VOLUME_MANAGER_RIGHT_ENUMERATE | SYS_VOLUME_MANAGER_RIGHT_OPEN |
|
|
SYS_VOLUME_MANAGER_RIGHT_WAIT;
|
|
startup[1].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[2].Tag = SYS_STARTUP_CAP_INPUT_MANAGER;
|
|
startup[2].SourceHandle = inputManager;
|
|
startup[2].DesiredRights = SYS_RIGHT_QUERY | SYS_INPUT_MANAGER_RIGHT_READ;
|
|
startup[2].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[3].Tag = SYS_STARTUP_CAP_DISPLAY_MANAGER;
|
|
startup[3].SourceHandle = displayManager;
|
|
startup[3].DesiredRights = SYS_RIGHT_DERIVE | SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE |
|
|
SYS_DISPLAY_MANAGER_RIGHT_MODESET | SYS_DISPLAY_MANAGER_RIGHT_CONSOLE;
|
|
startup[3].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
|
|
KSTATUS status = start_service_process(systemVolume, resourceContext, startup, 4,
|
|
&resourceService->Service);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE adminListener = SYS_HANDLE_INVALID;
|
|
SYS_HANDLE sessionControlListener = SYS_HANDLE_INVALID;
|
|
status = wait_resource_service_ready(&resourceService->Service, &adminListener, &sessionControlListener);
|
|
if (KSUCCESS(status))
|
|
status = verify_resource_service(adminListener, namespaceVolume);
|
|
if (!KSUCCESS(status)) {
|
|
if (adminListener != SYS_HANDLE_INVALID) (void)SysCloseHandle(adminListener);
|
|
if (sessionControlListener != SYS_HANDLE_INVALID) (void)SysCloseHandle(sessionControlListener);
|
|
debug_early_service_exit(&resourceService->Service);
|
|
(void)SysKillProcess(resourceService->Service.Process, 1);
|
|
(void)SysWait(resourceService->Service.Process, SYS_WAIT_FLAG_NONE);
|
|
close_service_handles(&resourceService->Service);
|
|
resourceService->Service.State = InitServiceFailed;
|
|
return status;
|
|
}
|
|
|
|
resourceService->AdminListener = adminListener;
|
|
resourceService->SessionControlListener = sessionControlListener;
|
|
resourceService->Service.State = InitServiceReady;
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Publishes DWM's desktop listener at the stable Session 0 desktop resource path.
|
|
static KSTATUS publish_dwm_desktop(SYS_HANDLE adminListener, SYS_HANDLE desktopListener) {
|
|
if (adminListener == SYS_HANDLE_INVALID || desktopListener == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
KSTATUS status = NtlResourceConnectListener(adminListener, &admin);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = NtlResourceBind(admin, SYS_SV_LITERAL(NTL_DESKTOP_SERVICE_PATH),
|
|
NTL_RESOURCE_KIND_CAPABILITY, desktopListener,
|
|
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
|
|
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
// Removes DWM's public desktop endpoint while retaining Session 0 itself inside RSSRV.
|
|
static void unpublish_dwm_desktop(SYS_HANDLE adminListener) {
|
|
if (adminListener == SYS_HANDLE_INVALID) return;
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
if (!KSUCCESS(NtlResourceConnectListener(adminListener, &admin))) return;
|
|
(void)NtlResourceUnbind(admin, SYS_SV_LITERAL(NTL_DESKTOP_SERVICE_PATH));
|
|
(void)NtlResourceClose(admin);
|
|
}
|
|
|
|
// Starts one DWM instance with physical presentation, cursor policy, and attenuated Session 0 authority.
|
|
static KSTATUS start_dwm_service(SYS_HANDLE systemVolume,
|
|
SYS_HANDLE displayManager,
|
|
SYS_HANDLE securityContext,
|
|
const INIT_RESOURCE_SERVICE* resourceService,
|
|
INIT_SERVICE* service) {
|
|
if (!resourceService || !service || !service->Descriptor || service->Descriptor->Type != InitServiceTypeDwm ||
|
|
displayManager == SYS_HANDLE_INVALID || securityContext == SYS_HANDLE_INVALID ||
|
|
resourceService->AdminListener == SYS_HANDLE_INVALID ||
|
|
resourceService->SessionControlListener == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
NTL_PROCESS_STARTUP_CAPABILITY startup[2];
|
|
memset(startup, 0, sizeof(startup));
|
|
startup[0].Tag = SYS_STARTUP_CAP_DISPLAY_MANAGER;
|
|
startup[0].SourceHandle = displayManager;
|
|
startup[0].DesiredRights = SYS_RIGHT_QUERY | SYS_DISPLAY_MANAGER_RIGHT_ENUMERATE |
|
|
SYS_DISPLAY_MANAGER_RIGHT_PRESENT | SYS_DISPLAY_MANAGER_RIGHT_CURSOR;
|
|
startup[0].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
startup[1].Tag = SYS_STARTUP_CAP_SESSION_CONTROL;
|
|
startup[1].SourceHandle = resourceService->SessionControlListener;
|
|
startup[1].DesiredRights = SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
|
|
startup[1].DuplicateFlags = SYS_DUPLICATE_HANDLE_FLAG_NONE;
|
|
|
|
KSTATUS status = start_service_process(systemVolume, securityContext, startup, 2u, service);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] DWM spawn/delegation failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
return status;
|
|
}
|
|
|
|
SYS_HANDLE exports[1] = { SYS_HANDLE_INVALID };
|
|
uint32_t exportCount = 0;
|
|
status = wait_service_ready(service, exports, 1u, &exportCount);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] DWM READY wait failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
} else if (exportCount != 1u || exports[0] == SYS_HANDLE_INVALID) {
|
|
debug_print("[init] DWM READY contract invalid\n");
|
|
status = NTLSTATUS_FAILURE;
|
|
} else {
|
|
status = publish_dwm_desktop(resourceService->AdminListener, exports[0]);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] DWM desktop publication failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
}
|
|
}
|
|
if (exports[0] != SYS_HANDLE_INVALID) (void)SysCloseHandle(exports[0]);
|
|
|
|
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;
|
|
}
|
|
|
|
// Builds the canonical RSSRV publication path for one generic service identifier.
|
|
static KSTATUS build_generic_service_path(const INIT_SERVICE_DESCRIPTOR* descriptor,
|
|
char* pathOut,
|
|
size_t pathCapacity) {
|
|
static const char prefix[] = "/Services/";
|
|
if (!descriptor || !pathOut || pathCapacity == 0) return NTLSTATUS_INVALID_PARAMETER;
|
|
size_t prefixLength = sizeof(prefix) - 1u;
|
|
size_t idLength = strlen(descriptor->Id);
|
|
if (idLength == 0 || prefixLength + idLength + 1u > pathCapacity)
|
|
return NTLSTATUS_NAME_TOO_LONG;
|
|
memcpy(pathOut, prefix, prefixLength);
|
|
memcpy(pathOut + prefixLength, descriptor->Id, idLength + 1u);
|
|
return NTLSTATUS_SUCCESS;
|
|
}
|
|
|
|
// Publishes one generic service listener through RSSRV rather than the kernel service registry.
|
|
static KSTATUS publish_generic_service(SYS_HANDLE adminListener,
|
|
const INIT_SERVICE_DESCRIPTOR* descriptor,
|
|
SYS_HANDLE publicListener) {
|
|
if (adminListener == SYS_HANDLE_INVALID || !descriptor || publicListener == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
char path[64];
|
|
KSTATUS status = build_generic_service_path(descriptor, path, sizeof(path));
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
status = NtlResourceConnectListener(adminListener, &admin);
|
|
if (!KSUCCESS(status)) return status;
|
|
status = NtlResourceBind(admin, SysSvFromCstr(path), NTL_RESOURCE_KIND_CAPABILITY,
|
|
publicListener,
|
|
SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT,
|
|
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
|
|
(void)NtlResourceClose(admin);
|
|
return status;
|
|
}
|
|
|
|
// Removes one generic service publication before its process is stopped or forgotten.
|
|
static void unpublish_generic_service(SYS_HANDLE adminListener,
|
|
const INIT_SERVICE_DESCRIPTOR* descriptor) {
|
|
if (adminListener == SYS_HANDLE_INVALID || !descriptor) return;
|
|
char path[64];
|
|
if (!KSUCCESS(build_generic_service_path(descriptor, path, sizeof(path)))) return;
|
|
SYS_HANDLE admin = SYS_HANDLE_INVALID;
|
|
if (!KSUCCESS(NtlResourceConnectListener(adminListener, &admin))) return;
|
|
(void)NtlResourceUnbind(admin, SysSvFromCstr(path));
|
|
(void)NtlResourceClose(admin);
|
|
}
|
|
|
|
// Starts one generic service, validates its READY listener export, and publishes it through RSSRV.
|
|
static KSTATUS start_generic_service(SYS_HANDLE systemVolume,
|
|
SYS_HANDLE securityContext,
|
|
const INIT_RESOURCE_SERVICE* resourceService,
|
|
INIT_SERVICE* service) {
|
|
if (!resourceService || !service || !service->Descriptor ||
|
|
service->Descriptor->Type != InitServiceTypeGeneric ||
|
|
resourceService->AdminListener == SYS_HANDLE_INVALID)
|
|
return NTLSTATUS_INVALID_PARAMETER;
|
|
|
|
KSTATUS status = start_service_process(systemVolume, securityContext, (const NTL_PROCESS_STARTUP_CAPABILITY*)0,
|
|
0u, service);
|
|
if (!KSUCCESS(status)) return status;
|
|
|
|
SYS_HANDLE exports[1] = { SYS_HANDLE_INVALID };
|
|
uint32_t exportCount = 0;
|
|
status = wait_service_ready(service, exports, 1u, &exportCount);
|
|
if (KSUCCESS(status) && (exportCount != 1u || exports[0] == SYS_HANDLE_INVALID))
|
|
status = NTLSTATUS_FAILURE;
|
|
if (KSUCCESS(status))
|
|
status = publish_generic_service(resourceService->AdminListener, service->Descriptor, exports[0]);
|
|
if (exports[0] != SYS_HANDLE_INVALID) (void)SysCloseHandle(exports[0]);
|
|
|
|
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 shutdown of one managed service, falling back to process termination.
|
|
static void stop_service(INIT_SERVICE* service) {
|
|
if (!service || service->Process == SYS_HANDLE_INVALID) return;
|
|
|
|
service->State = InitServiceStopping;
|
|
|
|
NTL_SERVICE_CONTROL_MESSAGE message;
|
|
memset(&message, 0, sizeof(message));
|
|
message.Size = sizeof(message);
|
|
message.Version = NTL_SERVICE_CONTROL_VERSION;
|
|
message.Operation = NTL_SERVICE_CONTROL_STOP;
|
|
message.ExportCount = 0;
|
|
|
|
SYS_SEND_PORT_REQUEST send;
|
|
memset(&send, 0, sizeof(send));
|
|
send.Size = sizeof(send);
|
|
send.EndpointHandle = service->ControlEndpoint;
|
|
send.Flags = SYS_PORT_FLAG_NONE;
|
|
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);
|
|
(void)SysWait(service->Process, SYS_WAIT_FLAG_NONE);
|
|
}
|
|
close_service_handles(service);
|
|
service->State = InitServiceStopped;
|
|
}
|
|
|
|
// Unpublishes and stops DWM so new desktop connections cannot race service teardown.
|
|
static void stop_dwm_service(INIT_RESOURCE_SERVICE* resourceService, INIT_SERVICE* service) {
|
|
if (!resourceService || !service || service->State != InitServiceReady) return;
|
|
unpublish_dwm_desktop(resourceService->AdminListener);
|
|
stop_service(service);
|
|
}
|
|
|
|
// Unpublishes and stops one generic service before RSSRV namespace teardown.
|
|
static void stop_generic_service(const INIT_RESOURCE_SERVICE* resourceService, INIT_SERVICE* service) {
|
|
if (!resourceService || !service || service->State != InitServiceReady) return;
|
|
unpublish_generic_service(resourceService->AdminListener, service->Descriptor);
|
|
stop_service(service);
|
|
}
|
|
|
|
// Stops ready generic services in reverse dependency order.
|
|
static void stop_generic_services(const INIT_RESOURCE_SERVICE* resourceService,
|
|
INIT_SERVICE* services) {
|
|
if (!resourceService || !services) return;
|
|
for (uint32_t order = gServiceConfig.StartupCount; order > 0; --order) {
|
|
uint16_t index = gServiceConfig.StartupOrder[order - 1u];
|
|
if (gServiceConfig.Services[index].Type == InitServiceTypeGeneric)
|
|
stop_generic_service(resourceService, &services[index]);
|
|
}
|
|
}
|
|
|
|
// Requests graceful RSSRV shutdown and releases its private administrative export.
|
|
static void stop_resource_service(INIT_RESOURCE_SERVICE* resourceService) {
|
|
if (!resourceService) return;
|
|
stop_service(&resourceService->Service);
|
|
if (resourceService->AdminListener != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(resourceService->AdminListener);
|
|
if (resourceService->SessionControlListener != SYS_HANDLE_INVALID)
|
|
(void)SysCloseHandle(resourceService->SessionControlListener);
|
|
resourceService->AdminListener = SYS_HANDLE_INVALID;
|
|
resourceService->SessionControlListener = SYS_HANDLE_INVALID;
|
|
}
|
|
|
|
// 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(SYS_STARTUP_CAP_SYSTEM_VOLUME);
|
|
SYS_HANDLE namespaceVolume = SysGetStartupCapability(SYS_STARTUP_CAP_SYSTEM_VOLUME_NAMESPACE);
|
|
SYS_HANDLE volumeManager = SysGetStartupCapability(SYS_STARTUP_CAP_VOLUME_MANAGER);
|
|
SYS_HANDLE inputManager = SysGetStartupCapability(SYS_STARTUP_CAP_INPUT_MANAGER);
|
|
SYS_HANDLE displayManager = SysGetStartupCapability(SYS_STARTUP_CAP_DISPLAY_MANAGER);
|
|
if (systemVolume == SYS_HANDLE_INVALID || namespaceVolume == SYS_HANDLE_INVALID ||
|
|
volumeManager == SYS_HANDLE_INVALID || inputManager == SYS_HANDLE_INVALID ||
|
|
displayManager == SYS_HANDLE_INVALID) {
|
|
debug_print("[init] FATAL: bootstrap resource-plane capability missing\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 resourceContext = SYS_HANDLE_INVALID;
|
|
status = derive_resource_service_context(&resourceContext);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: cannot derive RSSRV security context: ");
|
|
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");
|
|
(void)SysCloseHandle(resourceContext);
|
|
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);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(1);
|
|
}
|
|
|
|
INIT_RESOURCE_SERVICE resourceService;
|
|
initialize_resource_service(&resourceService,
|
|
&gServiceConfig.Services[gServiceConfig.ResourceServiceIndex]);
|
|
|
|
INIT_SERVICE dwmService;
|
|
memset(&dwmService, 0, sizeof(dwmService));
|
|
if (gServiceConfig.DwmServiceIndex != UINT16_MAX)
|
|
initialize_service(&dwmService, &gServiceConfig.Services[gServiceConfig.DwmServiceIndex]);
|
|
else {
|
|
dwmService.Process = SYS_HANDLE_INVALID;
|
|
dwmService.ControlEndpoint = SYS_HANDLE_INVALID;
|
|
dwmService.State = InitServiceStopped;
|
|
}
|
|
|
|
INIT_SERVICE genericServices[INIT_SERVICE_MAX_COUNT];
|
|
memset(genericServices, 0, sizeof(genericServices));
|
|
uint32_t automaticGenericCount = 0;
|
|
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
|
if (gServiceConfig.Services[i].Type != InitServiceTypeGeneric) continue;
|
|
initialize_service(&genericServices[i], &gServiceConfig.Services[i]);
|
|
if (gServiceConfig.Services[i].StartPolicy == InitServiceStartAuto) ++automaticGenericCount;
|
|
}
|
|
if (automaticGenericCount + 3u > SYS_WAIT_MANY_MAX_HANDLES) {
|
|
debug_print("[init] FATAL: too many automatic generic services for supervision wait set\n");
|
|
(void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(1);
|
|
}
|
|
|
|
for (uint32_t order = 0; order < gServiceConfig.StartupCount; ++order) {
|
|
uint16_t index = gServiceConfig.StartupOrder[order];
|
|
const INIT_SERVICE_DESCRIPTOR* descriptor = &gServiceConfig.Services[index];
|
|
if (descriptor->Type == InitServiceTypeResource) {
|
|
status = start_resource_service(systemVolume, namespaceVolume, volumeManager, inputManager,
|
|
displayManager, resourceContext, &resourceService);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: RSSRV startup failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(1);
|
|
}
|
|
debug_print("[init] RSSRV ready\n");
|
|
} else if (descriptor->Type == InitServiceTypeDwm) {
|
|
status = start_dwm_service(systemVolume, displayManager, unprivilegedServiceContext, &resourceService, &dwmService);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] noncritical DWM startup failed: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
} else {
|
|
debug_print("[init] DWM ready\n");
|
|
}
|
|
} else if (descriptor->Type == InitServiceTypeGeneric) {
|
|
status = start_generic_service(systemVolume, unprivilegedServiceContext, &resourceService,
|
|
&genericServices[index]);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] noncritical generic service startup failed: ");
|
|
debug_print(descriptor->Id);
|
|
debug_print(" status=");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
} else {
|
|
debug_print("[init] generic service ready: ");
|
|
debug_print(descriptor->Id);
|
|
debug_print("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resourceService.Service.State != InitServiceReady) {
|
|
debug_print("[init] FATAL: configured resource service was not started\n");
|
|
if (dwmService.State == InitServiceReady) stop_dwm_service(&resourceService, &dwmService);
|
|
stop_generic_services(&resourceService, genericServices);
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(1);
|
|
}
|
|
|
|
for (;;) {
|
|
SYS_HANDLE child = SYS_HANDLE_INVALID;
|
|
status = start_configured_child(systemVolume, childPath, &child);
|
|
if (!KSUCCESS(status)) {
|
|
debug_print("[init] FATAL: failed to start configured child: ");
|
|
debug_status(status);
|
|
debug_print("\n");
|
|
if (dwmService.State == InitServiceReady) stop_dwm_service(&resourceService, &dwmService);
|
|
stop_generic_services(&resourceService, genericServices);
|
|
stop_resource_service(&resourceService);
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
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] = resourceService.Service.Process;
|
|
kinds[waitCount++] = 0u;
|
|
if (dwmService.State == InitServiceReady && dwmService.Process != SYS_HANDLE_INVALID) {
|
|
waits[waitCount] = dwmService.Process;
|
|
kinds[waitCount++] = 1u;
|
|
}
|
|
waits[waitCount] = child;
|
|
kinds[waitCount++] = 2u;
|
|
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
|
if (gServiceConfig.Services[i].Type != InitServiceTypeGeneric ||
|
|
genericServices[i].State != InitServiceReady ||
|
|
genericServices[i].Process == SYS_HANDLE_INVALID)
|
|
continue;
|
|
waits[waitCount] = genericServices[i].Process;
|
|
kinds[waitCount++] = 3u + i;
|
|
}
|
|
|
|
uint32_t signaled = 0;
|
|
SYS_WAIT_MANY_REQUEST wait;
|
|
memset(&wait, 0, sizeof(wait));
|
|
wait.Size = sizeof(wait);
|
|
wait.Flags = SYS_WAIT_FLAG_NONE;
|
|
wait.Handles = waits;
|
|
wait.Count = waitCount;
|
|
wait.SignaledIndexOut = &signaled;
|
|
wait.TimeoutMilliseconds = SYS_WAIT_TIMEOUT_INFINITE;
|
|
status = SysWaitMany(&wait);
|
|
if (!KSUCCESS(status)) {
|
|
(void)SysKillProcess(child, 1);
|
|
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
|
|
(void)SysCloseHandle(child);
|
|
if (dwmService.State == InitServiceReady) stop_dwm_service(&resourceService, &dwmService);
|
|
stop_generic_services(&resourceService, genericServices);
|
|
stop_resource_service(&resourceService);
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
debug_print("[init] FATAL: service wait failed\n");
|
|
exit_now(1);
|
|
}
|
|
|
|
if (kinds[signaled] == 0u) {
|
|
int32_t serviceExit = -1;
|
|
(void)SysGetProcessExitCode(resourceService.Service.Process, &serviceExit);
|
|
resourceService.Service.State = InitServiceFailed;
|
|
debug_print("[init] FATAL: critical RSSRV exited unexpectedly: ");
|
|
debug_i32(serviceExit);
|
|
debug_print("\n");
|
|
(void)SysKillProcess(child, 1);
|
|
(void)SysWait(child, SYS_WAIT_FLAG_NONE);
|
|
(void)SysCloseHandle(child);
|
|
if (dwmService.State == InitServiceReady) stop_dwm_service(&resourceService, &dwmService);
|
|
for (uint32_t i = 0; i < gServiceConfig.ServiceCount; ++i) {
|
|
if (gServiceConfig.Services[i].Type == InitServiceTypeGeneric &&
|
|
genericServices[i].Process != SYS_HANDLE_INVALID) {
|
|
(void)SysKillProcess(genericServices[i].Process, 1);
|
|
(void)SysWait(genericServices[i].Process, SYS_WAIT_FLAG_NONE);
|
|
close_service_handles(&genericServices[i]);
|
|
genericServices[i].State = InitServiceFailed;
|
|
}
|
|
}
|
|
close_resource_service(&resourceService);
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(1);
|
|
}
|
|
|
|
if (kinds[signaled] == 1u) {
|
|
int32_t serviceExit = -1;
|
|
(void)SysGetProcessExitCode(dwmService.Process, &serviceExit);
|
|
debug_print("[init] noncritical DWM exited unexpectedly: ");
|
|
debug_i32(serviceExit);
|
|
debug_print("\n");
|
|
unpublish_dwm_desktop(resourceService.AdminListener);
|
|
close_service_handles(&dwmService);
|
|
dwmService.State = InitServiceFailed;
|
|
continue;
|
|
}
|
|
|
|
if (kinds[signaled] >= 3u) {
|
|
uint32_t index = kinds[signaled] - 3u;
|
|
INIT_SERVICE* generic = &genericServices[index];
|
|
int32_t serviceExit = -1;
|
|
(void)SysGetProcessExitCode(generic->Process, &serviceExit);
|
|
debug_print("[init] noncritical generic service exited unexpectedly: ");
|
|
debug_print(generic->Descriptor->Id);
|
|
debug_print(" code=");
|
|
debug_i32(serviceExit);
|
|
debug_print("\n");
|
|
unpublish_generic_service(resourceService.AdminListener, generic->Descriptor);
|
|
close_service_handles(generic);
|
|
generic->State = InitServiceFailed;
|
|
continue;
|
|
}
|
|
|
|
int32_t exitCode = -1;
|
|
status = SysGetProcessExitCode(child, &exitCode);
|
|
(void)SysCloseHandle(child);
|
|
child = SYS_HANDLE_INVALID;
|
|
if (!KSUCCESS(status)) exitCode = 1;
|
|
if (exitCode == 0) {
|
|
if (dwmService.State == InitServiceReady) stop_dwm_service(&resourceService, &dwmService);
|
|
stop_generic_services(&resourceService, genericServices);
|
|
stop_resource_service(&resourceService);
|
|
debug_print("[init] done\n");
|
|
if (unprivilegedServiceContext != SYS_HANDLE_INVALID) (void)SysCloseHandle(unprivilegedServiceContext);
|
|
(void)SysCloseHandle(resourceContext);
|
|
exit_now(0);
|
|
}
|
|
debug_print("[init] session process crashed; restarting\n");
|
|
restartChild = true;
|
|
break;
|
|
}
|
|
|
|
if (!restartChild) break;
|
|
}
|
|
|
|
exit_now(1);
|
|
}
|