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

685 lines
30 KiB
C

// Init service-configuration parser. Malformed or ambiguous service policy is rejected at boot.
#include "service_config.h"
#include <ntlite/bootstrap_capabilities.h>
#include <stddef.h>
#include <string.h>
#include <ntlite/status.h>
#define INIT_SERVICE_KEY_NAME 0x00000001u
#define INIT_SERVICE_KEY_IMAGE 0x00000002u
#define INIT_SERVICE_KEY_START 0x00000004u
#define INIT_SERVICE_KEY_CRITICAL 0x00000008u
#define INIT_SERVICE_KEY_RESTART 0x00000010u
#define INIT_SERVICE_KEY_READY_TIMEOUT 0x00000020u
#define INIT_SERVICE_KEY_STOP_TIMEOUT 0x00000040u
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
#define INIT_SERVICE_KEY_GRANTS 0x00000100u
#define INIT_SERVICE_KEY_EXPORTS 0x00000200u
#define INIT_SERVICE_KEY_AFTER 0x00000400u
#define INIT_SERVICE_KEY_PRIVILEGES 0x00000800u
#define INIT_SERVICE_KEY_RESTART_LIMIT 0x00001000u
#define INIT_SERVICE_KEY_RESTART_WINDOW 0x00002000u
#define INIT_SERVICE_KEY_RESTART_DELAY 0x00004000u
typedef struct INIT_PARSED_SERVICE {
INIT_SERVICE_DESCRIPTOR Descriptor;
char DependencyIds[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u];
uint32_t DependencyCount;
char AfterIds[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u];
uint32_t AfterCount;
uint32_t PresentKeys;
uint32_t SectionLine;
} INIT_PARSED_SERVICE;
static char gServicesIniBuffer[INIT_SERVICES_MAX_FILE_BYTES + 1u];
static INIT_PARSED_SERVICE gParsedServices[INIT_SERVICE_MAX_COUNT];
static char gSessionImage[SYS_PATH_MAX];
// Returns whether one byte is horizontal configuration whitespace.
static bool is_space(char c) {
return c == ' ' || c == '\t' || c == '\r';
}
// Trims leading horizontal whitespace in place by returning the first content byte.
static char* trim_left(char* text) {
while (*text && is_space(*text)) ++text;
return text;
}
// Trims trailing horizontal whitespace from one mutable string.
static void trim_right(char* text) {
char* end = text + strlen(text);
while (end > text && is_space(end[-1])) *--end = '\0';
}
// Returns whether one service identifier byte belongs to the deliberately small grammar.
static bool is_service_id_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.';
}
// Validates a case-sensitive service identifier used by sections and dependencies.
static bool validate_service_id(const char* id) {
size_t length = id ? strlen(id) : 0;
if (length == 0 || length > INIT_SERVICE_ID_MAX) return false;
for (size_t i = 0; i < length; ++i)
if (!is_service_id_char(id[i])) return false;
return true;
}
// Validates the capability-relative native image path accepted by the process loader.
static bool validate_image_path(const char* path) {
size_t length = path ? strlen(path) : 0;
if (length == 0 || length >= SYS_PATH_MAX || path[0] == '/' || path[length - 1u] == '/' ||
!SysSvIsUtf8Text(SysSvFromParts(path, length)))
return false;
const char* component = path;
for (size_t i = 0; i <= length; ++i) {
char c = path[i];
if (c == '\\' || c == ':') return false;
if (c != '/' && c != '\0') continue;
size_t componentLength = (size_t)(&path[i] - component);
if (componentLength == 0 ||
(componentLength == 1 && component[0] == '.') ||
(componentLength == 2 && component[0] == '.' && component[1] == '.'))
return false;
component = &path[i + 1u];
}
return true;
}
// Copies one bounded configuration string into fixed init-owned storage.
static bool copy_bounded(char* destination, size_t capacity, const char* source) {
size_t length = source ? strlen(source) : 0;
if (!destination || capacity == 0 || length >= capacity) return false;
memcpy(destination, source, length + 1u);
return true;
}
// Parses one strict lower-case boolean.
static bool parse_bool(const char* text, bool* valueOut) {
if (!text || !valueOut) return false;
if (strcmp(text, "true") == 0) {
*valueOut = true;
return true;
}
if (strcmp(text, "false") == 0) {
*valueOut = false;
return true;
}
return false;
}
// Parses one bounded unsigned decimal integer without signs or suffixes.
static bool parse_u32(const char* text, uint32_t minimum, uint32_t maximum, uint32_t* valueOut) {
if (!text || !*text || !valueOut) return false;
uint64_t value = 0;
for (const char* p = text; *p; ++p) {
if (*p < '0' || *p > '9') return false;
value = value * 10u + (uint32_t)(*p - '0');
if (value > maximum) return false;
}
if (value < minimum) return false;
*valueOut = (uint32_t)value;
return true;
}
// Looks up a parsed service by its case-sensitive identifier.
static int32_t find_parsed_service(const INIT_PARSED_SERVICE* services,
uint32_t serviceCount,
const char* id) {
for (uint32_t i = 0; i < serviceCount; ++i)
if (strcmp(services[i].Descriptor.Id, id) == 0) return (int32_t)i;
return -1;
}
// Parses distinct service IDs for one dependency edge type.
static bool parse_edges(char* value,
char ids[INIT_SERVICE_DEPENDENCY_MAX][INIT_SERVICE_ID_MAX + 1u],
uint32_t* countOut) {
*countOut = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* id = trim_left(cursor);
trim_right(id);
if (!validate_service_id(id) || *countOut >= INIT_SERVICE_DEPENDENCY_MAX)
return false;
for (uint32_t i = 0; i < *countOut; ++i)
if (strcmp(ids[i], id) == 0) return false;
if (!copy_bounded(ids[*countOut], sizeof(ids[0]), id)) return false;
++*countOut;
if (!comma) break;
cursor = comma + 1u;
if (!*cursor) return false;
}
return true;
}
static bool parse_privileges(char* value, SYS_PRIVILEGES* privilegesOut) {
static const struct { const char* Name; SYS_PRIVILEGES Bit; } names[] = {
{ "process-open-any", SYS_PRIV_PROCESS_OPEN_ANY },
{ "process-enumerate", SYS_PRIV_PROCESS_ENUMERATE },
{ "thread-enumerate-any", SYS_PRIV_THREAD_ENUMERATE_ANY },
{ "kernel-diagnostics", SYS_PRIV_KERNEL_DIAGNOSTICS },
{ "system-power", SYS_PRIV_SYSTEM_POWER },
{ "audio-access", SYS_PRIV_AUDIO_ACCESS },
{ "audio-master-control", SYS_PRIV_AUDIO_MASTER_CONTROL },
{ "console-access", SYS_PRIV_CONSOLE_ACCESS },
{ "display-access", SYS_PRIV_DISPLAY_ACCESS },
{ "debug-output", SYS_PRIV_DEBUG_OUTPUT },
{ "raw-storage", SYS_PRIV_RAW_STORAGE },
{ "filesystem-provider", SYS_PRIV_FILESYSTEM_PROVIDER },
{ "network-access", SYS_PRIV_NETWORK_ACCESS }
};
*privilegesOut = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* name = trim_left(cursor);
trim_right(name);
bool found = false;
for (uint32_t i = 0; i < sizeof(names) / sizeof(names[0]); ++i) {
if (strcmp(names[i].Name, name) != 0) continue;
if (*privilegesOut & names[i].Bit) return false;
*privilegesOut |= names[i].Bit;
found = true;
break;
}
if (!found) return false;
if (!comma) break;
cursor = comma + 1u;
if (!*cursor) return false;
}
return true;
}
// Parses explicit capability-source names without associating them with service roles.
static bool parse_grants(char* value, INIT_PARSED_SERVICE* service) {
service->Descriptor.GrantCount = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* name = trim_left(cursor);
trim_right(name);
size_t length = strlen(name);
if (length == 0 || length > INIT_SERVICE_GRANT_NAME_MAX ||
service->Descriptor.GrantCount >= INIT_SERVICE_GRANT_MAX) return false;
for (size_t i = 0; i < length; ++i)
if (!is_service_id_char(name[i])) return false;
if (!strchr(name, '.') || name[0] == '.' || name[length - 1u] == '.') return false;
if (strcmp(name, "service.control") == 0 || strncmp(name, "publish.", 8u) == 0)
return false;
for (uint32_t i = 0; i < service->Descriptor.GrantCount; ++i)
if (strcmp(service->Descriptor.Grants[i], name) == 0) return false;
if (!copy_bounded(service->Descriptor.Grants[service->Descriptor.GrantCount],
sizeof(service->Descriptor.Grants[0]), name)) return false;
++service->Descriptor.GrantCount;
if (!comma) break;
cursor = comma + 1;
if (!*cursor) return false;
}
return true;
}
// Reads the declared READY export names, which are an exact startup contract.
static bool parse_exports(char* value, INIT_PARSED_SERVICE* service) {
service->Descriptor.ExportCount = 0;
if (!*value) return true;
char* cursor = value;
for (;;) {
char* comma = strchr(cursor, ',');
if (comma) *comma = '\0';
char* name = trim_left(cursor);
trim_right(name);
if (!NtlServiceExportNameValid(name) ||
service->Descriptor.ExportCount >= INIT_SERVICE_EXPORT_MAX) return false;
for (uint32_t i = 0; i < service->Descriptor.ExportCount; ++i)
if (strcmp(name, service->Descriptor.Exports[i]) == 0) return false;
if (!copy_bounded(service->Descriptor.Exports[service->Descriptor.ExportCount],
sizeof(service->Descriptor.Exports[0]), name)) return false;
++service->Descriptor.ExportCount;
if (!comma) break;
cursor = comma + 1;
if (!*cursor) return false;
}
return true;
}
// Applies one known key to the current service and rejects duplicates or invalid values.
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
uint32_t bit = 0;
if (strcmp(key, "name") == 0) {
bit = INIT_SERVICE_KEY_NAME;
if (!*value || !copy_bounded(service->Descriptor.Name, sizeof(service->Descriptor.Name), value))
return false;
} else if (strcmp(key, "image") == 0) {
bit = INIT_SERVICE_KEY_IMAGE;
if (!validate_image_path(value) ||
!copy_bounded(service->Descriptor.ImagePath, sizeof(service->Descriptor.ImagePath), value))
return false;
} else if (strcmp(key, "start") == 0) {
bit = INIT_SERVICE_KEY_START;
if (strcmp(value, "auto") == 0) service->Descriptor.StartPolicy = InitServiceStartAuto;
else if (strcmp(value, "delayed") == 0) service->Descriptor.StartPolicy = InitServiceStartDelayed;
else if (strcmp(value, "disabled") == 0) service->Descriptor.StartPolicy = InitServiceStartDisabled;
else return false;
} else if (strcmp(key, "critical") == 0) {
bit = INIT_SERVICE_KEY_CRITICAL;
if (!parse_bool(value, &service->Descriptor.Critical)) return false;
} else if (strcmp(key, "restart") == 0) {
bit = INIT_SERVICE_KEY_RESTART;
if (strcmp(value, "never") == 0) service->Descriptor.RestartPolicy = InitServiceRestartNever;
else if (strcmp(value, "on-failure") == 0) service->Descriptor.RestartPolicy = InitServiceRestartOnFailure;
else if (strcmp(value, "always") == 0) service->Descriptor.RestartPolicy = InitServiceRestartAlways;
else return false;
} else if (strcmp(key, "restart_limit") == 0) {
bit = INIT_SERVICE_KEY_RESTART_LIMIT;
if (!parse_u32(value, 1u, 16u, &service->Descriptor.RestartLimit)) return false;
} else if (strcmp(key, "restart_window_ms") == 0) {
bit = INIT_SERVICE_KEY_RESTART_WINDOW;
if (!parse_u32(value, 1u, 3600000u, &service->Descriptor.RestartWindowMilliseconds)) return false;
} else if (strcmp(key, "restart_delay_ms") == 0) {
bit = INIT_SERVICE_KEY_RESTART_DELAY;
if (!parse_u32(value, 0u, 600000u, &service->Descriptor.RestartDelayMilliseconds)) return false;
} else if (strcmp(key, "ready_timeout_ms") == 0) {
bit = INIT_SERVICE_KEY_READY_TIMEOUT;
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.ReadyTimeoutMilliseconds)) return false;
} else if (strcmp(key, "stop_timeout_ms") == 0) {
bit = INIT_SERVICE_KEY_STOP_TIMEOUT;
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.StopTimeoutMilliseconds)) return false;
} else if (strcmp(key, "requires") == 0) {
bit = INIT_SERVICE_KEY_REQUIRES;
if (!parse_edges(value, service->DependencyIds, &service->DependencyCount)) return false;
} else if (strcmp(key, "after") == 0) {
bit = INIT_SERVICE_KEY_AFTER;
if (!parse_edges(value, service->AfterIds, &service->AfterCount)) return false;
} else if (strcmp(key, "privileges") == 0) {
bit = INIT_SERVICE_KEY_PRIVILEGES;
if (!parse_privileges(value, &service->Descriptor.Privileges)) return false;
} else if (strcmp(key, "grants") == 0) {
bit = INIT_SERVICE_KEY_GRANTS;
if (!parse_grants(value, service)) return false;
} else if (strcmp(key, "exports") == 0) {
bit = INIT_SERVICE_KEY_EXPORTS;
if (!parse_exports(value, service)) return false;
} else {
return false;
}
if (service->PresentKeys & bit) return false;
service->PresentKeys |= bit;
return true;
}
// Parses the strict services.ini grammar into temporary fixed storage.
static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint32_t* errorLineOut) {
uint32_t serviceCount = 0;
INIT_PARSED_SERVICE* current = NULL;
bool inSession = false;
bool sessionSeen = false;
bool sessionImageSeen = false;
uint32_t lineNumber = 0;
char* line = buffer;
while (*line) {
++lineNumber;
char* end = strchr(line, '\n');
if (!end) end = line + strlen(line);
char saved = *end;
*end = '\0';
char* text = trim_left(line);
trim_right(text);
bool valid = true;
if (!*text || *text == ';' || *text == '#') {
// Blank and whole-line comments are the only ignored line forms.
} else if (*text == '[') {
size_t length = strlen(text);
if (length < 3u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
valid = false;
} else {
text[length - 1u] = '\0';
const char prefix[] = "service.";
if (strcmp(text + 1, "session") == 0) {
if (sessionSeen) valid = false;
else {
sessionSeen = true;
inSession = true;
current = NULL;
}
} else if (strncmp(text + 1, prefix, sizeof(prefix) - 1u) != 0) {
valid = false;
} else {
inSession = false;
char* id = text + 1u + sizeof(prefix) - 1u;
if (!validate_service_id(id) || serviceCount >= INIT_SERVICE_MAX_COUNT ||
find_parsed_service(gParsedServices, serviceCount, id) >= 0) {
valid = false;
} else {
current = &gParsedServices[serviceCount++];
memset(current, 0, sizeof(*current));
current->SectionLine = lineNumber;
current->Descriptor.StartPolicy = InitServiceStartAuto;
current->Descriptor.RestartPolicy = InitServiceRestartNever;
current->Descriptor.ReadyTimeoutMilliseconds = 5000u;
current->Descriptor.StopTimeoutMilliseconds = 5000u;
current->Descriptor.RestartLimit = 3u;
current->Descriptor.RestartWindowMilliseconds = 300000u;
if (!copy_bounded(current->Descriptor.Id, sizeof(current->Descriptor.Id), id) ||
!copy_bounded(current->Descriptor.Name, sizeof(current->Descriptor.Name), id))
valid = false;
}
}
}
} else {
if (!current && !inSession) {
valid = false;
} else {
char* equals = strchr(text, '=');
if (!equals || strchr(equals + 1, '=')) {
valid = false;
} else {
*equals = '\0';
char* key = text;
char* value = trim_left(equals + 1);
trim_right(key);
trim_right(value);
if (inSession) {
if (strcmp(key, "image") != 0 || sessionImageSeen ||
!validate_image_path(value) ||
!copy_bounded(gSessionImage, sizeof(gSessionImage), value)) valid = false;
else sessionImageSeen = true;
} else if (!*key || !parse_service_key(current, key, value)) {
valid = false;
}
}
}
}
*end = saved;
if (!valid) {
if (errorLineOut) *errorLineOut = lineNumber;
return NTLSTATUS_INVALID_PARAMETER;
}
line = saved ? end + 1u : end;
}
if (serviceCount == 0 || !sessionImageSeen)
return NTLSTATUS_INVALID_PARAMETER;
*serviceCountOut = serviceCount;
return NTLSTATUS_SUCCESS;
}
// Resolves dependency identifiers, validates invariants, and computes dependency-first order.
static KSTATUS validate_services(uint32_t serviceCount,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut) {
memset(configOut, 0, sizeof(*configOut));
memcpy(configOut->SessionImage, gSessionImage, strlen(gSessionImage) + 1u);
static const char* const bootGrants[] = {
NTL_BOOT_CAP_NAMESPACE_ROOT, NTL_BOOT_CAP_VOLUME_MANAGER, NTL_BOOT_CAP_INPUT_MANAGER,
NTL_BOOT_CAP_DISPLAY_POLICY, NTL_BOOT_CAP_DISPLAY_PRESENT, NTL_BOOT_CAP_RESOURCE_REGISTRATION
};
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* descriptor = &gParsedServices[i].Descriptor;
for (uint32_t g = 0; g < descriptor->GrantCount; ++g) {
const char* name = descriptor->Grants[g];
bool known = false;
for (uint32_t b = 0; b < sizeof(bootGrants) / sizeof(bootGrants[0]); ++b)
if (strcmp(name, bootGrants[b]) == 0) known = true;
if (known) continue;
if (strncmp(name, "boot.", 5u) == 0) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
// Service export sources require an explicit dependency; the longest
// matching service ID owns a qualified export name.
int32_t provider = -1;
size_t prefixLength = 0;
for (uint32_t j = 0; j < serviceCount; ++j) {
const char* id = gParsedServices[j].Descriptor.Id;
size_t length = strlen(id);
if (length > prefixLength && strncmp(name, id, length) == 0 &&
name[length] == '.' && name[length + 1u] != '\0') {
provider = (int32_t)j;
prefixLength = length;
}
}
bool declaredDependency = false;
if (provider >= 0 && (uint32_t)provider != i &&
gParsedServices[provider].Descriptor.StartPolicy != InitServiceStartDisabled) {
for (uint32_t d = 0; d < gParsedServices[i].DependencyCount; ++d)
if (strcmp(gParsedServices[i].DependencyIds[d],
gParsedServices[provider].Descriptor.Id) == 0)
declaredDependency = true;
}
if (declaredDependency) {
const char* exportName = name + prefixLength + 1u;
bool declaredExport = false;
const INIT_SERVICE_DESCRIPTOR* source = &gParsedServices[provider].Descriptor;
for (uint32_t e = 0; e < source->ExportCount; ++e)
if (strcmp(source->Exports[e], exportName) == 0) declaredExport = true;
if (!declaredExport) declaredDependency = false;
}
if (!declaredDependency) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
}
// Exactly one critical automatic service owns the primordial namespace authority.
uint32_t namespaceProviders = 0;
for (uint32_t i = 0; i < serviceCount; ++i) {
const INIT_SERVICE_DESCRIPTOR* descriptor = &gParsedServices[i].Descriptor;
bool namespaceRoot = false;
bool registration = false;
for (uint32_t g = 0; g < descriptor->GrantCount; ++g) {
if (strcmp(descriptor->Grants[g], NTL_BOOT_CAP_NAMESPACE_ROOT) == 0) namespaceRoot = true;
if (strcmp(descriptor->Grants[g], NTL_BOOT_CAP_RESOURCE_REGISTRATION) == 0) registration = true;
}
if (!namespaceRoot && !registration) continue;
if (!namespaceRoot || !registration || !descriptor->Critical ||
descriptor->StartPolicy != InitServiceStartAuto) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
++namespaceProviders;
}
if (namespaceProviders != 1u) return NTLSTATUS_INVALID_PARAMETER;
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_PARSED_SERVICE* parsed = &gParsedServices[i];
if ((parsed->PresentKeys & INIT_SERVICE_KEY_IMAGE) == 0u) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
*descriptor = parsed->Descriptor;
for (uint32_t e = 0; e < descriptor->ExportCount; ++e) {
if (strlen(descriptor->Id) + 1u + strlen(descriptor->Exports[e]) >
INIT_SERVICE_GRANT_NAME_MAX) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_NAME_TOO_LONG;
}
}
descriptor->DependencyCount = parsed->DependencyCount;
for (uint32_t d = 0; d < parsed->DependencyCount; ++d) {
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->DependencyIds[d]);
if (dependency < 0 || (uint32_t)dependency == i) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
descriptor->Dependencies[d] = (uint16_t)dependency;
}
descriptor->AfterCount = parsed->AfterCount;
for (uint32_t d = 0; d < parsed->AfterCount; ++d) {
int32_t dependency = find_parsed_service(gParsedServices, serviceCount, parsed->AfterIds[d]);
if (dependency < 0 || (uint32_t)dependency == i) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
for (uint32_t r = 0; r < descriptor->DependencyCount; ++r) {
if (descriptor->Dependencies[r] == (uint16_t)dependency) {
if (errorLineOut) *errorLineOut = parsed->SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
descriptor->After[d] = (uint16_t)dependency;
}
}
for (uint32_t i = 0; i < serviceCount; ++i) {
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
if (descriptor->Critical && descriptor->StartPolicy != InitServiceStartAuto) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
if (descriptor->StartPolicy == InitServiceStartDisabled) continue;
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d) {
INIT_SERVICE_DESCRIPTOR* dependency = &configOut->Services[descriptor->Dependencies[d]];
if (dependency->StartPolicy == InitServiceStartDisabled ||
(descriptor->StartPolicy == InitServiceStartAuto &&
dependency->StartPolicy != InitServiceStartAuto)) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
for (uint32_t d = 0; d < descriptor->AfterCount; ++d) {
INIT_SERVICE_DESCRIPTOR* dependency = &configOut->Services[descriptor->After[d]];
if (descriptor->StartPolicy == InitServiceStartAuto &&
dependency->StartPolicy == InitServiceStartDelayed) {
if (errorLineOut) *errorLineOut = gParsedServices[i].SectionLine;
return NTLSTATUS_INVALID_PARAMETER;
}
}
}
uint32_t indegree[INIT_SERVICE_MAX_COUNT] = {0};
bool emitted[INIT_SERVICE_MAX_COUNT] = {0};
for (uint32_t i = 0; i < serviceCount; ++i)
indegree[i] = configOut->Services[i].DependencyCount + configOut->Services[i].AfterCount;
uint32_t emittedCount = 0;
while (emittedCount < serviceCount) {
int32_t next = -1;
for (uint32_t i = 0; i < serviceCount; ++i) {
if (!emitted[i] && indegree[i] == 0u) {
next = (int32_t)i;
break;
}
}
if (next < 0) {
if (errorLineOut) {
for (uint32_t i = 0; i < serviceCount; ++i) {
if (!emitted[i]) {
*errorLineOut = gParsedServices[i].SectionLine;
break;
}
}
}
return NTLSTATUS_DEADLOCK;
}
emitted[next] = true;
if (configOut->Services[next].StartPolicy != InitServiceStartDisabled)
configOut->StartupOrder[configOut->StartupCount++] = (uint16_t)next;
++emittedCount;
for (uint32_t i = 0; i < serviceCount; ++i) {
if (emitted[i]) continue;
INIT_SERVICE_DESCRIPTOR* descriptor = &configOut->Services[i];
for (uint32_t d = 0; d < descriptor->DependencyCount; ++d)
if (descriptor->Dependencies[d] == (uint16_t)next) --indegree[i];
for (uint32_t d = 0; d < descriptor->AfterCount; ++d)
if (descriptor->After[d] == (uint16_t)next) --indegree[i];
}
}
configOut->ServiceCount = serviceCount;
if (configOut->StartupCount + 2u > SYS_WAIT_MANY_MAX_HANDLES)
return NTLSTATUS_LIMIT_REACHED;
return NTLSTATUS_SUCCESS;
}
// Reads the mandatory services.ini file from the bootstrap system volume.
static KSTATUS read_services_ini(SYS_HANDLE systemVolume, size_t* sizeOut) {
SYS_HANDLE file = SYS_HANDLE_INVALID;
SYS_OPEN_FILE_AT_REQUEST open = SysMakeOpenFileAtRequest(systemVolume,
SYS_SV_LITERAL(INIT_SERVICES_PATH),
SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING,
SYS_FILE_FLAG_NONE,
&file);
KSTATUS status = SysOpenFileAt(&open);
if (!KSUCCESS(status)) return status;
uint64_t fileSize = 0;
status = SysGetFileSize(file, &fileSize);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(file);
return status;
}
if (fileSize == 0u || fileSize > INIT_SERVICES_MAX_FILE_BYTES) {
(void)SysCloseHandle(file);
return fileSize > INIT_SERVICES_MAX_FILE_BYTES ? NTLSTATUS_LIMIT_REACHED : NTLSTATUS_INVALID_PARAMETER;
}
size_t total = 0;
while (total < (size_t)fileSize) {
size_t transferred = 0;
SYS_FILE_IO_REQUEST read = SysMakeFileIoRequest(file,
gServicesIniBuffer + total,
(size_t)fileSize - total,
&transferred);
status = SysReadFile(&read);
if (!KSUCCESS(status) || transferred == 0u) {
(void)SysCloseHandle(file);
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
}
total += transferred;
}
(void)SysCloseHandle(file);
for (size_t i = 0; i < total; ++i)
if (gServicesIniBuffer[i] == '\0') return NTLSTATUS_INVALID_PARAMETER;
gServicesIniBuffer[total] = '\0';
*sizeOut = total;
return NTLSTATUS_SUCCESS;
}
// Loads, strictly parses, and validates services.ini beneath the bootstrap system volume.
KSTATUS InitLoadServiceConfig(SYS_HANDLE systemVolume,
INIT_SERVICE_CONFIG* configOut,
uint32_t* errorLineOut) {
if (systemVolume == SYS_HANDLE_INVALID || !configOut) return NTLSTATUS_INVALID_PARAMETER;
if (errorLineOut) *errorLineOut = 0;
memset(gParsedServices, 0, sizeof(gParsedServices));
memset(gSessionImage, 0, sizeof(gSessionImage));
size_t size = 0;
KSTATUS status = read_services_ini(systemVolume, &size);
if (!KSUCCESS(status)) return status;
(void)size;
uint32_t serviceCount = 0;
status = parse_services_ini(gServicesIniBuffer, &serviceCount, errorLineOut);
if (!KSUCCESS(status)) return status;
return validate_services(serviceCount, configOut, errorLineOut);
}