replace nob with ms' build, move disk creation to build.bat, major comment cleanup, remove old and stupid tests, overhaul interrupt management with per-CPU vector allocation, affinity migration and safe teardown, add proper IOAPIC routing and PCI MSI/MSI-X/INTx support, migrate drivers to the new interrupt API, integrate ACPICA with a proper OS services layer, move ACPI firmware events, power button handling and S5 shutdown into acpi.sys, add parallel driver startup and atomic publication, replace PnP arrays with dynamic lists, redesign service startup again to be asynchronous, add delayed startup mode for services, allow omitting keys in services.ini, fix acpi ec deferred work handling, fix kernel threads having garbage exit code.
This commit is contained in:
1 parent
d36f19509c
commit
36eef8f58d
696 files changed
+147281
-7530
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#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
|
||||
@@ -16,18 +17,25 @@
|
||||
#define INIT_SERVICE_KEY_REQUIRES 0x00000080u
|
||||
#define INIT_SERVICE_KEY_GRANTS 0x00000100u
|
||||
#define INIT_SERVICE_KEY_EXPORTS 0x00000200u
|
||||
#define INIT_SERVICE_REQUIRED_KEYS 0x000003FEu
|
||||
#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) {
|
||||
@@ -129,29 +137,66 @@ static int32_t find_parsed_service(const INIT_PARSED_SERVICE* services,
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Parses the comma-separated hard dependency list for one service.
|
||||
static bool parse_requires(char* value, INIT_PARSED_SERVICE* service) {
|
||||
service->DependencyCount = 0;
|
||||
// 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) || service->DependencyCount >= INIT_SERVICE_DEPENDENCY_MAX)
|
||||
if (!validate_service_id(id) || *countOut >= INIT_SERVICE_DEPENDENCY_MAX)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < service->DependencyCount; ++i)
|
||||
if (strcmp(service->DependencyIds[i], id) == 0) return false;
|
||||
if (!copy_bounded(service->DependencyIds[service->DependencyCount],
|
||||
sizeof(service->DependencyIds[0]), id))
|
||||
return false;
|
||||
++service->DependencyCount;
|
||||
|
||||
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 + 1;
|
||||
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;
|
||||
@@ -215,7 +260,11 @@ static bool parse_exports(char* value, INIT_PARSED_SERVICE* service) {
|
||||
static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* value) {
|
||||
uint32_t bit = 0;
|
||||
|
||||
if (strcmp(key, "image") == 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))
|
||||
@@ -223,6 +272,7 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
} 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) {
|
||||
@@ -232,7 +282,17 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
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;
|
||||
@@ -241,7 +301,13 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
if (!parse_u32(value, 1u, 600000u, &service->Descriptor.StopTimeoutMilliseconds)) return false;
|
||||
} else if (strcmp(key, "requires") == 0) {
|
||||
bit = INIT_SERVICE_KEY_REQUIRES;
|
||||
if (!parse_requires(value, service)) return false;
|
||||
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;
|
||||
@@ -261,6 +327,9 @@ static bool parse_service_key(INIT_PARSED_SERVICE* service, char* key, char* val
|
||||
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;
|
||||
|
||||
@@ -279,14 +348,22 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
// Blank and whole-line comments are the only ignored line forms.
|
||||
} else if (*text == '[') {
|
||||
size_t length = strlen(text);
|
||||
if (length < 11u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
|
||||
if (length < 3u || text[length - 1u] != ']' || strchr(text + 1, ']') != &text[length - 1u]) {
|
||||
valid = false;
|
||||
} else {
|
||||
text[length - 1u] = '\0';
|
||||
const char prefix[] = "service.";
|
||||
if (strncmp(text + 1, prefix, sizeof(prefix) - 1u) != 0) {
|
||||
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) {
|
||||
@@ -295,13 +372,20 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
current = &gParsedServices[serviceCount++];
|
||||
memset(current, 0, sizeof(*current));
|
||||
current->SectionLine = lineNumber;
|
||||
if (!copy_bounded(current->Descriptor.Id, sizeof(current->Descriptor.Id), id))
|
||||
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) {
|
||||
if (!current && !inSession) {
|
||||
valid = false;
|
||||
} else {
|
||||
char* equals = strchr(text, '=');
|
||||
@@ -313,7 +397,14 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
char* value = trim_left(equals + 1);
|
||||
trim_right(key);
|
||||
trim_right(value);
|
||||
if (!*key || !parse_service_key(current, key, value)) valid = false;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,7 +417,7 @@ static KSTATUS parse_services_ini(char* buffer, uint32_t* serviceCountOut, uint3
|
||||
line = saved ? end + 1u : end;
|
||||
}
|
||||
|
||||
if (serviceCount == 0)
|
||||
if (serviceCount == 0 || !sessionImageSeen)
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
*serviceCountOut = serviceCount;
|
||||
return NTLSTATUS_SUCCESS;
|
||||
@@ -337,6 +428,7 @@ 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,
|
||||
@@ -369,7 +461,7 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
bool declaredDependency = false;
|
||||
if (provider >= 0 && (uint32_t)provider != i &&
|
||||
gParsedServices[provider].Descriptor.StartPolicy == InitServiceStartAuto) {
|
||||
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)
|
||||
@@ -390,9 +482,29 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
}
|
||||
|
||||
// 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_REQUIRED_KEYS) {
|
||||
if ((parsed->PresentKeys & INIT_SERVICE_KEY_IMAGE) == 0u) {
|
||||
if (errorLineOut) *errorLineOut = parsed->SectionLine;
|
||||
return NTLSTATUS_INVALID_PARAMETER;
|
||||
}
|
||||
@@ -415,14 +527,43 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
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->StartPolicy != InitServiceStartAuto) continue;
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
@@ -432,7 +573,7 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
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;
|
||||
indegree[i] = configOut->Services[i].DependencyCount + configOut->Services[i].AfterCount;
|
||||
|
||||
uint32_t emittedCount = 0;
|
||||
while (emittedCount < serviceCount) {
|
||||
@@ -456,19 +597,17 @@ static KSTATUS validate_services(uint32_t serviceCount,
|
||||
}
|
||||
|
||||
emitted[next] = true;
|
||||
if (configOut->Services[next].StartPolicy == InitServiceStartAuto)
|
||||
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];
|
||||
break;
|
||||
}
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,6 +671,7 @@ KSTATUS InitLoadServiceConfig(SYS_HANDLE systemVolume,
|
||||
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;
|
||||
|
||||
Reference in new issue
Block a user