Files
nt-lite/code/user/personalities/posix/posix_runtime.c
T

808 lines
34 KiB
C

// POSIX personality process/thread runtime state backed by the native TEB extension slot.
#include <stdlib.h>
#include <string.h>
#include <ntlite/posix_service.h>
#include <ntlite/process_loader.h>
#include <ntlite/path.h>
#include <ntlite/resource.h>
#include <ntlite/status.h>
#include "posix_internal.h"
#include "posix_fd.h"
static POSIX_PROCESS_STATE* gPosixProcessState;
static int gEmergencyErrno;
NTL_POSIX_API char** environ;
// Reports one startup failure without depending on the POSIX descriptor layer.
static void PosixRuntimeReportStartupFailure(const char* stage, KSTATUS status) {
static const char hex[] = "0123456789ABCDEF";
char buffer[128];
const char* prefix = "posix startup: ";
size_t position = 0;
while (*prefix && position + 1u < sizeof(buffer)) buffer[position++] = *prefix++;
while (stage && *stage && position + 1u < sizeof(buffer)) buffer[position++] = *stage++;
const char* middle = " failed status=0x";
while (*middle && position + 1u < sizeof(buffer)) buffer[position++] = *middle++;
for (int shift = 28; shift >= 0 && position + 1u < sizeof(buffer); shift -= 4)
buffer[position++] = hex[(status >> shift) & 0xFu];
if (position + 1u < sizeof(buffer)) buffer[position++] = '\n';
buffer[position] = '\0';
SYS_WRITE_CONSOLE_REQUEST write;
memset(&write, 0, sizeof(write));
write.Size = sizeof(write);
write.Text = SysSvFromParts(buffer, position);
(void)SysWriteConsole(&write);
}
static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process);
// Copies one immutable loader vector into mutable process-local POSIX strings.
static KSTATUS PosixRuntimeCopyVector(int environment, char*** vectorOut, int* countOut) {
if (!vectorOut || !countOut) return NTLSTATUS_INVALID_PARAMETER;
*vectorOut = (char**)0;
*countOut = 0;
uint32_t count = 0;
KSTATUS status = environment ? NtlGetProcessEnvironmentCount(&count)
: NtlGetProcessArgumentCount(&count);
if (!KSUCCESS(status)) return status;
if (count > INT32_MAX) return NTLSTATUS_LIMIT_REACHED;
char** vector = (char**)calloc((size_t)count + 1u, sizeof(char*));
if (!vector) return NTLSTATUS_NO_MEMORY;
for (uint32_t i = 0; i < count; ++i) {
SYS_STRING_VIEW view = SYS_SV_NULL;
status = environment ? NtlGetProcessEnvironment(i, &view)
: NtlGetProcessArgument(i, &view);
if (!KSUCCESS(status)) {
for (uint32_t j = 0; j < i; ++j) free(vector[j]);
free(vector);
return status;
}
vector[i] = (char*)malloc(view.Length + 1u);
if (!vector[i]) {
for (uint32_t j = 0; j < i; ++j) free(vector[j]);
free(vector);
return NTLSTATUS_NO_MEMORY;
}
if (view.Length) memcpy(vector[i], view.Data, view.Length);
vector[i][view.Length] = '\0';
}
*vectorOut = vector;
*countOut = (int)count;
return NTLSTATUS_SUCCESS;
}
// Builds the compatibility command-line view from the loader-owned argv vector.
static KSTATUS PosixRuntimeBuildCommandLine(POSIX_PROCESS_STATE* process) {
if (!process) return NTLSTATUS_INVALID_PARAMETER;
if (process->ArgumentCount <= 0 || !process->Arguments) return NTLSTATUS_SUCCESS;
size_t required = 1u;
for (int i = 0; i < process->ArgumentCount; ++i) {
const char* argument = process->Arguments[i] ? process->Arguments[i] : "";
int quote = 0;
for (const char* cursor = argument; *cursor; ++cursor) {
if (*cursor == ' ' || *cursor == '\t') quote = 1;
if (*cursor == '"') {
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
++required;
}
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
++required;
}
if (quote) {
if (required > (size_t)-1 - 2u) return NTLSTATUS_LIMIT_REACHED;
required += 2u;
}
if (i != 0) {
if (required == (size_t)-1) return NTLSTATUS_LIMIT_REACHED;
++required;
}
}
char* commandLine = (char*)malloc(required);
if (!commandLine) return NTLSTATUS_NO_MEMORY;
size_t position = 0;
for (int i = 0; i < process->ArgumentCount; ++i) {
const char* argument = process->Arguments[i] ? process->Arguments[i] : "";
int quote = 0;
for (const char* cursor = argument; *cursor; ++cursor) {
if (*cursor == ' ' || *cursor == '\t') { quote = 1; break; }
}
if (i != 0) commandLine[position++] = ' ';
if (quote) commandLine[position++] = '"';
for (const char* cursor = argument; *cursor; ++cursor) {
if (*cursor == '"') commandLine[position++] = '\\';
commandLine[position++] = *cursor;
}
if (quote) commandLine[position++] = '"';
}
commandLine[position] = '\0';
process->CommandLine = commandLine;
return NTLSTATUS_SUCCESS;
}
// Materializes argv and environ before application entry executes.
static KSTATUS PosixRuntimeInitializeVectors(POSIX_PROCESS_STATE* process) {
if (!process) return NTLSTATUS_INVALID_PARAMETER;
int environmentCount = 0;
KSTATUS status = PosixRuntimeCopyVector(0, &process->Arguments, &process->ArgumentCount);
if (!KSUCCESS(status)) return status;
status = PosixRuntimeCopyVector(1, &process->Environment, &environmentCount);
if (!KSUCCESS(status)) {
for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]);
free(process->Arguments);
process->Arguments = (char**)0;
process->ArgumentCount = 0;
return status;
}
environ = process->Environment;
status = PosixRuntimeBuildCommandLine(process);
if (!KSUCCESS(status)) {
PosixRuntimeDestroyVectors(process);
return status;
}
return NTLSTATUS_SUCCESS;
}
// Releases process-local argv/environ storage during initialization rollback.
static void PosixRuntimeDestroyVectors(POSIX_PROCESS_STATE* process) {
if (!process) return;
if (process->Arguments) {
for (int i = 0; i < process->ArgumentCount; ++i) free(process->Arguments[i]);
free(process->Arguments);
}
if (process->Environment) {
for (char** entry = process->Environment; *entry; ++entry) free(*entry);
free(process->Environment);
}
free(process->CommandLine);
process->Arguments = (char**)0;
process->Environment = (char**)0;
process->CommandLine = (char*)0;
process->ArgumentCount = 0;
environ = (char**)0;
}
// Resolves the published POSIX service and opens one private client endpoint.
static KSTATUS PosixRuntimeConnectServer(SYS_HANDLE* endpointOut) {
if (!endpointOut) return NTLSTATUS_INVALID_PARAMETER;
*endpointOut = SYS_HANDLE_INVALID;
SYS_HANDLE resource = SYS_HANDLE_INVALID;
KSTATUS status = NtlResourceConnect(&resource);
if (!KSUCCESS(status)) return status;
SYS_HANDLE listener = SYS_HANDLE_INVALID;
status = NtlResourceLookup(resource, SYS_SV_LITERAL(NTL_POSIX_SERVICE_PATH),
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT, &listener);
(void)NtlResourceClose(resource);
if (!KSUCCESS(status)) return status;
SYS_CONNECT_PORT_REQUEST connect;
memset(&connect, 0, sizeof(connect));
connect.Size = sizeof(connect);
connect.ListenerHandle = listener;
connect.Flags = SYS_PORT_CONNECT_FLAG_NONE;
connect.EndpointHandleOut = endpointOut;
status = SysConnectPort(&connect);
(void)SysCloseHandle(listener);
return status;
}
// Calls one fixed-size posixsrv request over an explicitly supplied endpoint.
static KSTATUS PosixRuntimeCallEndpoint(SYS_HANDLE endpoint,
const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
uint32_t requestHandleCount,
SYS_HANDLE* replyHandlesOut,
uint32_t replyHandleCapacity,
uint32_t expectedReplyHandleCount,
uint32_t* actualReplyHandleCountOut,
NTL_POSIX_REPLY* replyOut) {
if (endpoint == SYS_HANDLE_INVALID || !request || !replyOut ||
(expectedReplyHandleCount != UINT32_MAX && expectedReplyHandleCount > replyHandleCapacity) ||
(replyHandleCapacity != 0 && !replyHandlesOut))
return NTLSTATUS_INVALID_PARAMETER;
memset(replyOut, 0, sizeof(*replyOut));
if (replyHandlesOut) {
for (uint32_t i = 0; i < replyHandleCapacity; ++i)
replyHandlesOut[i] = SYS_HANDLE_INVALID;
}
SYS_PORT_MESSAGE_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_CALL_PORT_REQUEST call;
memset(&call, 0, sizeof(call));
call.Size = sizeof(call);
call.EndpointHandle = endpoint;
call.Flags = SYS_PORT_FLAG_NONE;
call.RequestPayload = request;
call.RequestLength = sizeof(*request);
call.RequestHandles = requestHandles;
call.RequestHandleCount = requestHandleCount;
call.ReplyPayload = replyOut;
call.ReplyCapacity = sizeof(*replyOut);
call.ReplyHandlesOut = replyHandlesOut;
call.ReplyHandleCapacity = replyHandleCapacity;
call.ReplyInfoOut = &info;
call.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
KSTATUS status = SysCallPort(&call);
if (!KSUCCESS(status)) return status;
uint32_t expectedHandles = KSUCCESS(replyOut->Status) ? expectedReplyHandleCount : 0u;
int handlesValid = expectedReplyHandleCount == UINT32_MAX ?
info.HandleCount <= replyHandleCapacity : info.HandleCount == expectedHandles;
if (info.Type != SYS_PORT_MESSAGE_REPLY || !handlesValid ||
info.PayloadLength != sizeof(*replyOut) || replyOut->Size != sizeof(*replyOut) ||
replyOut->Version != NTL_POSIX_PROTOCOL_VERSION ||
replyOut->Operation != request->Operation ||
replyOut->Flags != NTL_POSIX_REQUEST_FLAG_NONE) {
if (replyHandlesOut) {
uint32_t closeCount = info.HandleCount < replyHandleCapacity ?
info.HandleCount : replyHandleCapacity;
for (uint32_t i = 0; i < closeCount; ++i) {
if (replyHandlesOut[i] != SYS_HANDLE_INVALID)
(void)SysCloseHandle(replyHandlesOut[i]);
replyHandlesOut[i] = SYS_HANDLE_INVALID;
}
}
return NTLSTATUS_FAILURE;
}
if (actualReplyHandleCountOut) *actualReplyHandleCountOut = info.HandleCount;
return NTLSTATUS_SUCCESS;
}
// Calls posixsrv while optionally transferring native capabilities.
KSTATUS PosixRuntimeCallServerWithHandles(const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* handles,
uint32_t handleCount,
NTL_POSIX_REPLY* replyOut) {
POSIX_PROCESS_STATE* process = gPosixProcessState;
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC)
return NTLSTATUS_NOT_FOUND;
return PosixRuntimeCallEndpoint(process->ServerEndpoint, request, handles, handleCount,
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, replyOut);
}
// Calls posixsrv with independent request/reply capability transfer.
KSTATUS PosixRuntimeCallServerExchange(const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* requestHandles,
uint32_t requestHandleCount,
SYS_HANDLE* replyHandlesOut,
uint32_t replyHandleCapacity,
uint32_t expectedReplyHandleCount,
NTL_POSIX_REPLY* replyOut) {
POSIX_PROCESS_STATE* process = gPosixProcessState;
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC)
return NTLSTATUS_NOT_FOUND;
return PosixRuntimeCallEndpoint(process->ServerEndpoint, request,
requestHandles, requestHandleCount,
replyHandlesOut, replyHandleCapacity,
expectedReplyHandleCount, (uint32_t*)0, replyOut);
}
KSTATUS PosixRuntimeCallServerExchangeVariable(const NTL_POSIX_REQUEST* request,
SYS_HANDLE* replyHandlesOut,
uint32_t replyHandleCapacity,
uint32_t* replyHandleCountOut,
NTL_POSIX_REPLY* replyOut) {
POSIX_PROCESS_STATE* process = gPosixProcessState;
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || !replyHandleCountOut)
return NTLSTATUS_INVALID_PARAMETER;
*replyHandleCountOut = 0;
return PosixRuntimeCallEndpoint(process->ServerEndpoint, request,
(const SYS_PORT_HANDLE_TRANSFER*)0, 0,
replyHandlesOut, replyHandleCapacity, UINT32_MAX,
replyHandleCountOut, replyOut);
}
// Calls posixsrv over the registered process endpoint and validates one reply.
KSTATUS PosixRuntimeCallServer(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut) {
return PosixRuntimeCallServerWithHandles(request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
replyOut);
}
// Registers a temporary endpoint to the current native/POSIX process identity.
static KSTATUS PosixRuntimeRegisterAuxiliaryEndpoint(SYS_HANDLE endpoint) {
POSIX_PROCESS_STATE* process = gPosixProcessState;
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || endpoint == SYS_HANDLE_INVALID)
return NTLSTATUS_NOT_FOUND;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_REGISTER_PROCESS;
size_t currentDirectorySize = 0;
KSTATUS status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), &currentDirectorySize);
if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path))
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
request.PathLength = (uint32_t)strlen(request.Path);
NTL_POSIX_REPLY reply;
status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply);
if (!KSUCCESS(status)) return status;
if (!KSUCCESS(reply.Status)) return reply.Status;
if (reply.PosixPid != process->DefaultIdentity.PosixPid ||
reply.NativeGeneration != process->DefaultIdentity.Generation ||
(reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING &&
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING))
return NTLSTATUS_ACCESS_DENIED;
return NTLSTATUS_SUCCESS;
}
// Uses an auxiliary registered connection so one blocking request cannot stall ordinary POSIX calls.
KSTATUS PosixRuntimeCallServerBlockingWithHandles(const NTL_POSIX_REQUEST* request,
const SYS_PORT_HANDLE_TRANSFER* handles,
uint32_t handleCount,
NTL_POSIX_REPLY* replyOut) {
if (!request || !replyOut || (handleCount != 0 && !handles) ||
handleCount > SYS_PORT_MAX_TRANSFER_HANDLES)
return NTLSTATUS_INVALID_PARAMETER;
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
KSTATUS status = PosixRuntimeConnectServer(&endpoint);
if (!KSUCCESS(status)) return status;
status = PosixRuntimeRegisterAuxiliaryEndpoint(endpoint);
if (KSUCCESS(status))
status = PosixRuntimeCallEndpoint(endpoint, request, handles, handleCount,
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, replyOut);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
return status;
}
// Uses an auxiliary registered connection without transferring capabilities.
KSTATUS PosixRuntimeCallServerBlocking(const NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* replyOut) {
return PosixRuntimeCallServerBlockingWithHandles(
request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, replyOut);
}
// Queries the current server-owned POSIX process attributes.
KSTATUS PosixRuntimeGetProcessAttributes(NTL_POSIX_REPLY* replyOut) {
if (!replyOut) return NTLSTATUS_INVALID_PARAMETER;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_GET_PROCESS_ATTRIBUTES;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
KSTATUS status = PosixRuntimeCallServer(&request, replyOut);
if (!KSUCCESS(status)) return status;
return replyOut->Status;
}
// Reads and validates optional POSIX bootstrap identity from generic userspace loader data.
static KSTATUS PosixRuntimeReadBootstrap(NTL_POSIX_BOOTSTRAP_DATA* bootstrapOut) {
if (!bootstrapOut) return NTLSTATUS_INVALID_PARAMETER;
memset(bootstrapOut, 0, sizeof(*bootstrapOut));
size_t size = 0;
KSTATUS status = NtlGetProcessStartupData(bootstrapOut, sizeof(*bootstrapOut), &size);
if (status == NTLSTATUS_NOT_FOUND)
return NTLSTATUS_SUCCESS;
if (!KSUCCESS(status)) return status;
if (size != sizeof(*bootstrapOut) || bootstrapOut->Magic != NTL_POSIX_BOOTSTRAP_MAGIC ||
bootstrapOut->Version != NTL_POSIX_BOOTSTRAP_VERSION ||
bootstrapOut->Size != sizeof(*bootstrapOut) || bootstrapOut->Reserved != 0 ||
(bootstrapOut->InitialSignalMask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0 ||
(bootstrapOut->IgnoredSignals & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return NTLSTATUS_INVALID_PARAMETER;
bootstrapOut->InitialSignalMask &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
bootstrapOut->IgnoredSignals &= ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
return NTLSTATUS_SUCCESS;
}
// Registers the current native process with posixsrv and caches its stable logical identity.
static KSTATUS PosixRuntimeRegisterProcess(POSIX_PROCESS_STATE* process,
const NTL_POSIX_BOOTSTRAP_DATA* bootstrap) {
if (!process || !bootstrap) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = NTLSTATUS_SUCCESS;
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
status = PosixRuntimeConnectServer(&endpoint);
if (!KSUCCESS(status)) return status;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_REGISTER_PROCESS;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.TransactionKind = NTL_POSIX_TRANSACTION_NONE;
request.BootstrapToken = bootstrap->Token;
request.BootstrapPosixPid = bootstrap->PosixPid;
request.BootstrapGeneration = bootstrap->Generation;
size_t currentDirectorySize = 0;
status = NtlGetCurrentDirectory(request.Path, sizeof(request.Path), &currentDirectorySize);
if (!KSUCCESS(status) || currentDirectorySize == 0 || currentDirectorySize > sizeof(request.Path)) {
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
return KSUCCESS(status) ? NTLSTATUS_FAILURE : status;
}
request.PathLength = (uint32_t)strlen(request.Path);
NTL_POSIX_REPLY reply;
status = PosixRuntimeCallEndpoint(endpoint, &request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0,
(SYS_HANDLE*)0, 0, 0, (uint32_t*)0, &reply);
if (!KSUCCESS(status)) {
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
return status;
}
if (!KSUCCESS(reply.Status)) {
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
return reply.Status;
}
if (reply.ClientProcessId != SysGetCurrentPid() || reply.PosixPid == 0 ||
reply.PosixPid > 0x7FFFFFFFu || reply.NativeGeneration == 0 ||
(reply.ProcessState != NTL_POSIX_PROCESS_STATE_RUNNING &&
reply.ProcessState != NTL_POSIX_PROCESS_STATE_CONSTRUCTING &&
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_PREPARING &&
reply.ProcessState != NTL_POSIX_PROCESS_STATE_EXEC_COMMITTING)) {
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
return NTLSTATUS_FAILURE;
}
process->ServerEndpoint = endpoint;
process->DefaultIdentity.PosixPid = reply.PosixPid;
process->DefaultIdentity.Generation = reply.NativeGeneration;
return NTLSTATUS_SUCCESS;
}
// Initializes process-local POSIX state, registers stable identity, and attaches the initial thread.
KSTATUS PosixRuntimeInitializeProcess(void) {
if (gPosixProcessState)
return PosixRuntimeAttachThread();
SYS_PEB* peb = SysGetPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || peb->Version != SYS_ENV_VERSION)
return NTLSTATUS_INVALID_PARAMETER;
POSIX_PROCESS_STATE* process = (POSIX_PROCESS_STATE*)calloc(1, sizeof(*process));
if (!process)
return NTLSTATUS_NO_MEMORY;
process->Magic = POSIX_PROCESS_STATE_MAGIC;
process->Peb = peb;
process->ServerEndpoint = SYS_HANDLE_INVALID;
process->DefaultIdentity.FdLock = SYS_HANDLE_INVALID;
gPosixProcessState = process;
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
KSTATUS status = PosixRuntimeReadBootstrap(&bootstrap);
if (status != NTLSTATUS_SUCCESS) {
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
free(process);
return status;
}
status = PosixRuntimeRegisterProcess(process, &bootstrap);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("register-process", status);
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
free(process);
return status;
}
process->BootstrapToken = bootstrap.Token;
process->BootstrapPosixPid = bootstrap.PosixPid;
process->BootstrapGeneration = bootstrap.Generation;
status = PosixRuntimeInitializeVectors(process);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("vectors", status);
(void)SysDisconnectPort(process->ServerEndpoint);
(void)SysCloseHandle(process->ServerEndpoint);
process->ServerEndpoint = SYS_HANDLE_INVALID;
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
free(process);
return status;
}
for (int signalNumber = NTL_POSIX_SIGNAL_HUP;
signalNumber <= NTL_POSIX_SIGNAL_MAX; ++signalNumber) {
uint64_t bit = NTL_POSIX_SIGNAL_BIT(signalNumber);
if (bootstrap.IgnoredSignals & bit)
process->DefaultIdentity.SignalActions[signalNumber].Handler = (uintptr_t)SIG_IGN;
}
status = PosixFdInitializeLogicalProcess(&process->DefaultIdentity, bootstrap.FdInheritanceToken);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("fd-init", status);
PosixRuntimeDestroyVectors(process);
(void)SysDisconnectPort(process->ServerEndpoint);
(void)SysCloseHandle(process->ServerEndpoint);
process->ServerEndpoint = SYS_HANDLE_INVALID;
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
free(process);
return status;
}
status = PosixRuntimeAttachThreadWithSignalMask(bootstrap.InitialSignalMask);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("thread-attach", status);
PosixFdDestroyLogicalProcess(&process->DefaultIdentity);
PosixRuntimeDestroyVectors(process);
(void)SysDisconnectPort(process->ServerEndpoint);
(void)SysCloseHandle(process->ServerEndpoint);
process->ServerEndpoint = SYS_HANDLE_INVALID;
gPosixProcessState = (POSIX_PROCESS_STATE*)0;
free(process);
return status;
}
return NTLSTATUS_SUCCESS;
}
// Returns the POSIX state for the current native process after startup.
POSIX_PROCESS_STATE* PosixRuntimeProcessState(void) {
POSIX_PROCESS_STATE* process = gPosixProcessState;
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC)
return (POSIX_PROCESS_STATE*)0;
return process;
}
// Registers the current native thread capability with posixsrv.
static KSTATUS PosixRuntimeRegisterThread(POSIX_PROCESS_STATE* process, POSIX_THREAD_STATE* thread) {
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC || !thread)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
if (!KSUCCESS(status)) return status;
SYS_HANDLE selfThread = SYS_HANDLE_INVALID;
status = SysOpenCurrentThread(SYS_RIGHT_QUERY | SYS_RIGHT_TRANSFER | SYS_THREAD_RIGHT_NOTIFY,
&selfThread);
if (!KSUCCESS(status)) return status;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = selfThread;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_THREAD_RIGHT_NOTIFY;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_REGISTER_THREAD;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
request.SignalMask = thread->SignalBlocked;
request.SignalApcRoutine = (uint64_t)(uintptr_t)PosixSignalApc;
NTL_POSIX_REPLY reply;
status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
(void)SysCloseHandle(selfThread);
if (!KSUCCESS(status)) return status;
return reply.Status;
}
// Removes the current native thread from posixsrv's target registry.
static void PosixRuntimeUnregisterThread(POSIX_THREAD_STATE* thread) {
if (!thread || !thread->ServerRegistered ||
!thread->NativeProcess || thread->NativeProcess->Magic != POSIX_PROCESS_STATE_MAGIC)
return;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_UNREGISTER_THREAD;
request.Flags = NTL_POSIX_REQUEST_FLAG_NONE;
NTL_POSIX_REPLY reply;
(void)PosixRuntimeCallServer(&request, &reply);
thread->ServerRegistered = 0;
}
// Attaches the native thread with one inherited persistent POSIX signal mask.
KSTATUS PosixRuntimeAttachThreadWithSignalMask(uint64_t signalMask) {
SYS_TEB* teb = SysGetTeb();
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!teb || teb->Magic != SYS_TEB_MAGIC || !process)
return NTLSTATUS_INVALID_PARAMETER;
if (teb->SubsystemData) {
POSIX_THREAD_STATE* existing = (POSIX_THREAD_STATE*)teb->SubsystemData;
return existing->Magic == POSIX_THREAD_STATE_MAGIC &&
existing->NativeProcess == process && existing->LogicalProcess
? NTLSTATUS_SUCCESS
: NTLSTATUS_ACCESS_DENIED;
}
POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)calloc(1, sizeof(*thread));
if (!thread)
return NTLSTATUS_NO_MEMORY;
thread->Magic = POSIX_THREAD_STATE_MAGIC;
thread->NativeProcess = process;
thread->LogicalProcess = &process->DefaultIdentity;
thread->NativeThreadId = SysGetCurrentTid();
thread->SignalBlocked = signalMask & NTL_POSIX_SIGNAL_VALID_MASK &
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
if (thread->NativeThreadId == 0) {
free(thread);
return NTLSTATUS_FAILURE;
}
KSTATUS status = PosixRuntimeRegisterThread(process, thread);
if (!KSUCCESS(status)) {
thread->Magic = 0;
free(thread);
return status;
}
thread->ServerRegistered = 1u;
teb->SubsystemData = thread;
return NTLSTATUS_SUCCESS;
}
// Attaches a native thread with the default empty signal mask.
KSTATUS PosixRuntimeAttachThread(void) {
return PosixRuntimeAttachThreadWithSignalMask(0);
}
// Returns the current thread state, attaching lazily when necessary.
POSIX_THREAD_STATE* PosixRuntimeThreadState(void) {
if (!PosixRuntimeProcessState()) {
if (PosixRuntimeInitializeProcess() != NTLSTATUS_SUCCESS)
return (POSIX_THREAD_STATE*)0;
}
SYS_TEB* teb = SysGetTeb();
if (!teb || teb->Magic != SYS_TEB_MAGIC)
return (POSIX_THREAD_STATE*)0;
if (!teb->SubsystemData && PosixRuntimeAttachThread() != NTLSTATUS_SUCCESS)
return (POSIX_THREAD_STATE*)0;
POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)teb->SubsystemData;
if (!thread || thread->Magic != POSIX_THREAD_STATE_MAGIC ||
thread->NativeProcess != PosixRuntimeProcessState() || !thread->LogicalProcess)
return (POSIX_THREAD_STATE*)0;
return thread;
}
// Returns the logical POSIX process selected by the current native thread.
POSIX_LOGICAL_PROCESS_STATE* PosixRuntimeLogicalProcessState(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
return thread ? thread->LogicalProcess : (POSIX_LOGICAL_PROCESS_STATE*)0;
}
// Releases POSIX state owned by the current native TEB.
void PosixRuntimeDetachThread(void) {
SYS_TEB* teb = SysGetTeb();
if (!teb || teb->Magic != SYS_TEB_MAGIC || !teb->SubsystemData)
return;
POSIX_THREAD_STATE* thread = (POSIX_THREAD_STATE*)teb->SubsystemData;
if (thread->Magic != POSIX_THREAD_STATE_MAGIC ||
thread->NativeProcess != PosixRuntimeProcessState())
return;
PosixRuntimeUnregisterThread(thread);
teb->SubsystemData = (void*)0;
thread->Magic = 0;
free(thread);
}
// Returns the lvalue backing the current thread's errno value.
NTL_POSIX_API int* __ntl_posix_errno_location(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread)
return &gEmergencyErrno;
return &thread->ErrnoValue;
}
// Clears the current thread's dynamic-loader error state.
void PosixRuntimeClearDlError(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread)
return;
thread->DlError[0] = '\0';
thread->DlErrorPending = 0;
}
// Stores one dynamic-loader error in current-thread state.
void PosixRuntimeSetDlError(const char* message) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread)
return;
if (!message)
message = "dynamic loader error";
strncpy(thread->DlError, message, sizeof(thread->DlError) - 1u);
thread->DlError[sizeof(thread->DlError) - 1u] = '\0';
thread->DlErrorPending = 1;
}
// Returns and consumes the current thread's dynamic-loader error.
char* PosixRuntimeTakeDlError(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread || !thread->DlErrorPending || !thread->DlError[0])
return (char*)0;
thread->DlErrorPending = 0;
return thread->DlError;
}
// Commits a server-built child only after every current personality bootstrap step succeeds.
static KSTATUS PosixRuntimeReportBootstrapReady(POSIX_PROCESS_STATE* process) {
if (!process || process->Magic != POSIX_PROCESS_STATE_MAGIC)
return NTLSTATUS_INVALID_PARAMETER;
if (process->BootstrapToken == 0) return NTLSTATUS_SUCCESS;
NTL_POSIX_REQUEST ready;
memset(&ready, 0, sizeof(ready));
ready.Size = sizeof(ready);
ready.Version = NTL_POSIX_PROTOCOL_VERSION;
ready.Operation = NTL_POSIX_OP_BOOTSTRAP_READY;
ready.BootstrapToken = process->BootstrapToken;
ready.BootstrapPosixPid = process->BootstrapPosixPid;
ready.BootstrapGeneration = process->BootstrapGeneration;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServer(&ready, &reply);
if (!KSUCCESS(status)) return status;
if (!KSUCCESS(reply.Status)) return reply.Status;
process->BootstrapToken = 0;
process->BootstrapPosixPid = 0;
process->BootstrapGeneration = 0;
return NTLSTATUS_SUCCESS;
}
// Initializes all POSIX process state required before application main.
NTL_POSIX_API uint32_t __ntl_posix_process_start(void) {
KSTATUS status = PosixRuntimeInitializeProcess();
if (status != NTLSTATUS_SUCCESS)
return status;
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
status = PosixSignalInitializeExceptionTranslation(process);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("signal-init", status);
return status;
}
status = PosixRuntimeReportBootstrapReady(process);
if (status != NTLSTATUS_SUCCESS) {
PosixRuntimeReportStartupFailure("bootstrap-ready", status);
return status;
}
return NTLSTATUS_SUCCESS;
}
// Returns process-local vectors to the POSIX startup shim after initialization.
NTL_POSIX_API uint32_t __ntl_posix_process_vectors(int* argcOut,
char*** argvOut,
char*** envpOut) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process || !argcOut || !argvOut || !envpOut) return NTLSTATUS_INVALID_PARAMETER;
*argcOut = process->ArgumentCount;
*argvOut = process->Arguments;
*envpOut = process->Environment;
return NTLSTATUS_SUCCESS;
}
// Finds one process-local environment entry by exact variable name.
NTL_POSIX_API char* getenv(const char* name) {
if (!name || !name[0] || strchr(name, '=')) return (char*)0;
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process || !process->Environment) return (char*)0;
size_t nameLength = strlen(name);
for (char** entry = process->Environment; *entry; ++entry) {
if (strncmp(*entry, name, nameLength) == 0 && (*entry)[nameLength] == '=')
return *entry + nameLength + 1u;
}
return (char*)0;
}