move POSIX spawn, exec and child tracking into posixsrv, keep process transactions alive across reconnects and preserve stable PIDs across image replacement, flesh out spawn with ordered file actions, process-group selection and signal inheritance, implement execve with bootstrap validation and rollback before retiring the old process, add vfork children that share the native process while keeping separate POSIX descriptor and signal state, replace local spawn bookkeeping with child reaping, nonblocking and process-group waits and orphan cleanup, add signal dispositions, per-thread masks, pending delivery, process/group signalling and alarms, translate native exceptions into POSIX signals and inherit masks when creating threads, make pipe waits interruptible and raise SIGPIPE for broken pipes, pass arguments and environment into main and add getenv/environ, add direct user APC notifications that can run without an alertable wait, add current-thread capabilities and resume-and-wait, let accepted IPC endpoints open restricted peer process capabilities and preserve caller security during spawn and exec, make process preparation reusable and improve DLL lookup across current, executable and fallback directories, move keyboard repeat into RSSRV so PS/2 and USB share one repeat policy, fix video presentation, report launch failure stages and status codes, mooaaar tests.

This commit is contained in:
maksw committed 2026-09-23 09:39:42 +02:00
1 parent 73abd6f90d
commit 899225f551
57 files changed
+8817 -1216

No files matched your search

+615 -165
View File
@@ -1,8 +1,10 @@
// POSIX compatibility support for posix proc.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ntlite/syscall.h>
#include <ntlite/security.h>
#include <ntlite/path.h>
#include <ntlite/process_loader.h>
#include "posix_internal.h"
@@ -14,80 +16,16 @@ static SYS_PEB* PosixPeb(void) {
return process ? process->Peb : (SYS_PEB*)0;
}
// Implements the posix get command line operation.
// Returns the POSIX argv-derived command-line compatibility view when available.
__declspec(dllexport) const char* posix_get_command_line(void) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (process && process->CommandLine) return process->CommandLine;
SYS_PEB* peb = PosixPeb();
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
return peb->CommandLine.Data;
}
// Records one spawn association in process-local POSIX state.
static void ProcTableInsert(pid_t pid, SYS_HANDLE handle, uint64_t inheritanceToken) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) {
SysCloseHandle(handle);
return;
}
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (!process->SpawnTable[i].Used) {
process->SpawnTable[i] = (POSIX_SPAWN_ENTRY){ pid, handle, inheritanceToken, true };
return;
}
}
SysCloseHandle(handle);
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
}
// Finds a spawned process handle retained by the current POSIX process.
static SYS_HANDLE ProcTableFind(pid_t pid) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) return SYS_HANDLE_INVALID;
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid)
return process->SpawnTable[i].Handle;
}
return SYS_HANDLE_INVALID;
}
// Removes one spawn association from process-local POSIX state.
static void ProcTableRemove(pid_t pid) {
POSIX_PROCESS_STATE* process = PosixRuntimeProcessState();
if (!process) return;
for (uint32_t i = 0; i < POSIX_SPAWN_TABLE_SIZE; i++) {
if (process->SpawnTable[i].Used && process->SpawnTable[i].Pid == pid) {
uint64_t inheritanceToken = process->SpawnTable[i].FdInheritanceToken;
memset(&process->SpawnTable[i], 0, sizeof(process->SpawnTable[i]));
if (inheritanceToken != 0) PosixFdCancelInheritanceSnapshot(inheritanceToken);
return;
}
}
}
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
// Each token that contains a space is wrapped in double-quotes.
// argv[0] is included (it becomes the executable token visible to _start).
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
size_t pos = 0;
for (int i = 0; argv && argv[i]; i++) {
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
const char* tok = argv[i];
bool needQuote = false;
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
if (needQuote && pos < outSize - 1) out[pos++] = '"';
for (; *tok && pos < outSize - 2; tok++) {
if (*tok == '"') { out[pos++] = '\\'; }
out[pos++] = *tok;
}
if (needQuote && pos < outSize - 1) out[pos++] = '"';
}
if (pos < outSize) out[pos] = '\0';
}
// POSIX API.
// Returns the stable POSIX PID selected for the calling logical thread.
__declspec(dllexport) pid_t getpid(void) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread || !thread->LogicalProcess || thread->LogicalProcess->PosixPid == 0)
@@ -95,110 +33,622 @@ __declspec(dllexport) pid_t getpid(void) {
return (pid_t)thread->LogicalProcess->PosixPid;
}
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
const char* path,
const posix_spawn_file_actions_t* file_actions, // ignored v1
const posix_spawnattr_t* attrp, // ignored v1
char* const argv[],
char* const envp[]) // ignored v1
{
(void)file_actions; (void)attrp; (void)envp;
#define POSIX_SPAWN_ACTIONS_MAGIC 0x53464131u
#define POSIX_SPAWN_ATTRIBUTES_MAGIC 0x53415431u
char native[512];
uint32_t pathStatus = PosixProcessPath(path, native, sizeof(native));
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
SYS_HANDLE imageFile = SYS_HANDLE_INVALID;
pathStatus = NtlOpenFile(SysSvFromCstr(native), SYS_FILE_ACCESS_READ | SYS_FILE_ACCESS_QUERY,
SYS_FILE_OPEN_EXISTING, SYS_FILE_FLAG_NONE, &imageFile);
if (pathStatus != NTLSTATUS_SUCCESS) { errno = PosixStatusToErrno(pathStatus); return -1; }
char cmdline[512];
BuildCmdline(argv, cmdline, sizeof(cmdline));
// If argv was NULL or empty, fall back to just the path so _start still
// gets a meaningful argv[0].
if (cmdline[0] == '\0') {
size_t len = strlen(path);
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
memcpy(cmdline, path, len);
cmdline[len] = '\0';
}
uint64_t inheritanceToken = 0;
uint32_t snapshotStatus =
PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC, &inheritanceToken);
if (snapshotStatus != NTLSTATUS_SUCCESS) {
(void)SysCloseHandle(imageFile);
errno = PosixStatusToErrno(snapshotStatus);
return -1;
}
NTL_POSIX_BOOTSTRAP_DATA bootstrap;
memset(&bootstrap, 0, sizeof(bootstrap));
bootstrap.Magic = NTL_POSIX_BOOTSTRAP_MAGIC;
bootstrap.Version = NTL_POSIX_BOOTSTRAP_VERSION;
bootstrap.Size = sizeof(bootstrap);
bootstrap.FdInheritanceToken = inheritanceToken;
uint32_t procHandle = 0;
NTL_CREATE_SYSTEM_PROCESS_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Flags = NTL_PROCESS_LOAD_FLAG_NONE;
req.ImageFile = imageFile;
req.Path = SysSvFromCstr(native);
req.CommandLine = SysSvFromCstr(cmdline);
req.SecurityContext = SYS_SECURITY_CONTEXT_CURRENT;
req.ResolveModuleName = PosixResolveModuleName;
req.StartupData = &bootstrap;
req.StartupDataSize = sizeof(bootstrap);
req.ProcessHandleOut = &procHandle;
uint32_t s = NtlCreateSystemProcess(&req);
if (imageFile != SYS_HANDLE_INVALID) (void)SysCloseHandle(imageFile);
if (s != NTLSTATUS_SUCCESS) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
errno = PosixStatusToErrno(s);
return -1;
}
KSTATUS armStatus = PosixFdArmInheritanceSnapshot(inheritanceToken, procHandle);
if (!KSUCCESS(armStatus) && armStatus != NTLSTATUS_NOT_FOUND) {
(void)SysKillProcess(procHandle, (int32_t)NTLSTATUS_PROCESS_KILLED);
(void)SysCloseHandle(procHandle);
PosixFdCancelInheritanceSnapshot(inheritanceToken);
errno = PosixStatusToErrno(armStatus);
return -1;
}
uint32_t kpid = 0;
SysGetProcessPid(procHandle, &kpid);
ProcTableInsert((pid_t)kpid, procHandle, inheritanceToken);
if (pidOut) *pidOut = (pid_t)kpid;
// Converts one posixsrv transport/result pair into a POSIX construction error.
static int PosixConstructionCall(NTL_POSIX_REQUEST* request, NTL_POSIX_REPLY* reply) {
KSTATUS status = PosixRuntimeCallServer(request, reply);
if (!KSUCCESS(status)) return PosixStatusToErrno(status);
if (!KSUCCESS(reply->Status)) return PosixStatusToErrno(reply->Status);
return 0;
}
// options: WNOHANG not supported in v1 (always blocks).
__declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
(void)options;
SYS_HANDLE handle = ProcTableFind(pid);
if (handle == SYS_HANDLE_INVALID) { errno = ESRCH; return -1; }
SysWait(handle, SYS_WAIT_FLAG_NONE);
int32_t exitCode = 0;
SysGetProcessExitCode(handle, &exitCode);
if (status) *status = exitCode << 8; // WEXITSTATUS convention
SysCloseHandle(handle);
ProcTableRemove(pid);
return pid;
// Cancels one process-owned construction transaction after a client-side failure.
static void PosixConstructionCancelTransaction(uint32_t transactionId) {
if (!transactionId) 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_CANCEL_TRANSACTION;
request.TransactionId = transactionId;
NTL_POSIX_REPLY reply;
(void)PosixRuntimeCallServer(&request, &reply);
}
// Implements the exit operation.
// Begins one server-owned process-construction transaction.
static int PosixConstructionBegin(uint32_t kind, uint32_t* transactionIdOut) {
if (!transactionIdOut ||
(kind != NTL_POSIX_TRANSACTION_SPAWN && kind != NTL_POSIX_TRANSACTION_EXEC))
return EINVAL;
*transactionIdOut = 0;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_BEGIN_TRANSACTION;
request.TransactionKind = kind;
NTL_POSIX_REPLY reply;
int error = PosixConstructionCall(&request, &reply);
if (error) return error;
if (!reply.TransactionId) return EIO;
*transactionIdOut = reply.TransactionId;
return 0;
}
// Adds one bounded string length to the aggregate construction payload size.
static int PosixConstructionMeasureString(const char* text, uint32_t* totalBytes) {
if (!text || !totalBytes) return EINVAL;
size_t length = strlen(text);
if (length > NTL_POSIX_CONSTRUCT_DATA_MAX ||
(uint32_t)length > NTL_POSIX_CONSTRUCT_DATA_MAX - *totalBytes)
return E2BIG;
*totalBytes += (uint32_t)length;
return 0;
}
// Validates one argv/envp vector against the bounded server transaction format.
static int PosixConstructionMeasureVector(char* const vector[], uint32_t* totalBytes) {
if (!vector) return 0;
for (uint32_t index = 0; vector[index]; ++index) {
if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG;
int error = PosixConstructionMeasureString(vector[index], totalBytes);
if (error) return error;
}
return 0;
}
// Validates all bounded spawn text before allocating a server transaction.
static int PosixSpawnValidateInput(const char* path,
const posix_spawn_file_actions_t* actions,
char* const argv[],
char* const envp[]) {
if (!path || !path[0]) return EINVAL;
size_t pathLength = strlen(path);
if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG;
uint32_t totalBytes = 0;
int error = PosixConstructionMeasureString(path, &totalBytes);
if (error) return error;
error = PosixConstructionMeasureVector(argv, &totalBytes);
if (error) return error;
error = PosixConstructionMeasureVector(envp, &totalBytes);
if (error) return error;
if (!actions) return 0;
if (actions->Count > POSIX_SPAWN_FILE_ACTION_CAPACITY ||
actions->Count > NTL_POSIX_SPAWN_ACTION_MAX)
return EINVAL;
for (uint32_t i = 0; i < actions->Count; ++i) {
const posix_spawn_file_action_entry* action = &actions->Entries[i];
if (action->Type != NTL_POSIX_SPAWN_FILE_ACTION_OPEN) continue;
if (!action->Path) return EINVAL;
size_t length = strlen(action->Path);
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
error = PosixConstructionMeasureString(action->Path, &totalBytes);
if (error) return error;
}
return 0;
}
// Sends one construction executable pathname to posixsrv.
static int PosixConstructionSendExecutable(uint32_t transactionId, const char* path) {
if (!path || !path[0]) return EINVAL;
size_t length = strlen(path);
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_CONSTRUCT_SET_EXECUTABLE;
request.TransactionId = transactionId;
request.PathLength = (uint32_t)length;
memcpy(request.Path, path, length + 1u);
NTL_POSIX_REPLY reply;
return PosixConstructionCall(&request, &reply);
}
// Streams one argv/envp vector through bounded Port messages.
static int PosixConstructionSendVector(uint32_t transactionId, uint32_t kind, char* const vector[]) {
if (!vector) return 0;
for (uint32_t index = 0; vector[index]; ++index) {
if (index >= NTL_POSIX_CONSTRUCT_VECTOR_MAX) return E2BIG;
size_t length = strlen(vector[index]);
if (length > UINT32_MAX) return E2BIG;
uint32_t offset = 0;
do {
uint32_t remaining = (uint32_t)length - offset;
uint32_t chunk = remaining > SYS_PATH_MAX ? SYS_PATH_MAX : remaining;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_CONSTRUCT_WRITE_VECTOR;
request.TransactionId = transactionId;
request.ConstructionVectorKind = kind;
request.ConstructionVectorIndex = index;
request.ConstructionVectorOffset = offset;
request.PathLength = chunk;
if (chunk) memcpy(request.Path, vector[index] + offset, chunk);
request.Path[chunk] = '\0';
if (offset + chunk == (uint32_t)length)
request.ConstructionVectorFlags = NTL_POSIX_CONSTRUCT_VECTOR_CHUNK_FINAL;
NTL_POSIX_REPLY reply;
int error = PosixConstructionCall(&request, &reply);
if (error) return error;
offset += chunk;
} while (offset < (uint32_t)length || (length == 0 && offset == 0));
}
return 0;
}
// Sends ordered file actions to the server-owned logical descriptor manifest.
static int PosixSpawnSendFileActions(uint32_t transactionId,
const posix_spawn_file_actions_t* actions) {
if (!actions) return 0;
for (uint32_t i = 0; i < actions->Count; ++i) {
const posix_spawn_file_action_entry* action = &actions->Entries[i];
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_SPAWN_ADD_FILE_ACTION;
request.TransactionId = transactionId;
request.SpawnActionKind = action->Type;
request.SpawnFileDescriptor = action->FileDescriptor;
request.SpawnNewFileDescriptor = action->NewFileDescriptor;
request.SpawnOpenFlags = (uint32_t)action->OpenFlags;
request.SpawnOpenMode = (uint32_t)action->Mode;
if (action->Type == NTL_POSIX_SPAWN_FILE_ACTION_OPEN) {
if (!action->Path) return EINVAL;
size_t length = strlen(action->Path);
if (length > SYS_PATH_MAX) return ENAMETOOLONG;
request.PathLength = (uint32_t)length;
memcpy(request.Path, action->Path, length + 1u);
}
NTL_POSIX_REPLY reply;
int error = PosixConstructionCall(&request, &reply);
if (error) return error;
}
return 0;
}
// Sends process-group and signal inheritance policy for one server-owned spawn.
static int PosixSpawnSendAttributes(uint32_t transactionId, const posix_spawnattr_t* attributes) {
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread) return EIO;
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_SPAWN_SET_ATTRIBUTES;
request.TransactionId = transactionId;
request.SpawnSignalMask = thread->SignalBlocked;
if (attributes) {
request.SpawnAttributeFlags = attributes->Flags;
request.ProcessGroupId = (uint32_t)attributes->ProcessGroup;
if (attributes->Flags & POSIX_SPAWN_SETSIGMASK)
request.SpawnSignalMask = attributes->SignalMask;
if (attributes->Flags & POSIX_SPAWN_SETSIGDEF)
request.SpawnSignalDefaultMask = attributes->SignalDefault;
}
NTL_POSIX_REPLY reply;
return PosixConstructionCall(&request, &reply);
}
// Delegates the caller's native security authority so posixsrv does not spawn as itself.
static KSTATUS PosixProcessDeriveSecurityContext(SYS_HANDLE* contextOut) {
if (!contextOut) return NTLSTATUS_INVALID_PARAMETER;
*contextOut = SYS_HANDLE_INVALID;
SYS_SECURITY_CONTEXT_INFO info;
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
KSTATUS status = SysQuerySecurityContext(SYS_SECURITY_CONTEXT_CURRENT, &info);
if (!KSUCCESS(status)) return status;
if ((info.EffectivePrivileges & ~info.DelegablePrivileges) != 0 ||
(info.ServiceClaims & ~info.DelegableServiceClaims) != 0)
return NTLSTATUS_ACCESS_DENIED;
SYS_DERIVE_SECURITY_CONTEXT_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceContext = SYS_SECURITY_CONTEXT_CURRENT;
derive.EffectivePrivileges = info.EffectivePrivileges;
derive.DelegablePrivileges = info.DelegablePrivileges;
derive.ServiceClaims = info.ServiceClaims;
derive.DelegableServiceClaims = info.DelegableServiceClaims;
derive.Flags = SYS_SECURITY_CONTEXT_FLAG_NONE;
derive.ContextHandleOut = contextOut;
return SysDeriveSecurityContext(&derive);
}
// Initializes an empty ordered spawn file-action set.
__declspec(dllexport) int posix_spawn_file_actions_init(posix_spawn_file_actions_t* actions) {
if (!actions) return EINVAL;
memset(actions, 0, sizeof(*actions));
actions->Initialized = POSIX_SPAWN_ACTIONS_MAGIC;
return 0;
}
// Releases pathname storage owned by one file-action set.
__declspec(dllexport) int posix_spawn_file_actions_destroy(posix_spawn_file_actions_t* actions) {
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL;
for (uint32_t i = 0; i < actions->Count; ++i) free(actions->Entries[i].Path);
memset(actions, 0, sizeof(*actions));
return 0;
}
// Appends one descriptor close action.
__declspec(dllexport) int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t* actions,
int fd) {
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0) return EBADF;
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
memset(action, 0, sizeof(*action));
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_CLOSE;
action->FileDescriptor = fd;
return 0;
}
// Appends one dup2 action; the destination loses FD_CLOEXEC during server application.
__declspec(dllexport) int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t* actions,
int fd, int newFd) {
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || newFd < 0)
return EBADF;
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
memset(action, 0, sizeof(*action));
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_DUP2;
action->FileDescriptor = fd;
action->NewFileDescriptor = newFd;
return 0;
}
// Appends one open-at-descriptor action and owns a private copy of its pathname.
__declspec(dllexport) int posix_spawn_file_actions_addopen(posix_spawn_file_actions_t* actions,
int fd, const char* path,
int openFlags, mode_t mode) {
if (!actions || actions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC || fd < 0 || !path)
return EINVAL;
if (actions->Count >= POSIX_SPAWN_FILE_ACTION_CAPACITY) return ENOMEM;
size_t length = strlen(path);
char* copy = (char*)malloc(length + 1u);
if (!copy) return ENOMEM;
memcpy(copy, path, length + 1u);
posix_spawn_file_action_entry* action = &actions->Entries[actions->Count++];
memset(action, 0, sizeof(*action));
action->Type = NTL_POSIX_SPAWN_FILE_ACTION_OPEN;
action->FileDescriptor = fd;
action->OpenFlags = openFlags;
action->Mode = mode;
action->Path = copy;
return 0;
}
// Initializes one POSIX spawn attribute object.
__declspec(dllexport) int posix_spawnattr_init(posix_spawnattr_t* attributes) {
if (!attributes) return EINVAL;
memset(attributes, 0, sizeof(*attributes));
attributes->Initialized = POSIX_SPAWN_ATTRIBUTES_MAGIC;
return 0;
}
// Invalidates one spawn attribute object.
__declspec(dllexport) int posix_spawnattr_destroy(posix_spawnattr_t* attributes) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL;
memset(attributes, 0, sizeof(*attributes));
return 0;
}
// Returns active spawn attribute flags.
__declspec(dllexport) int posix_spawnattr_getflags(const posix_spawnattr_t* attributes,
short* flags) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !flags)
return EINVAL;
*flags = (short)attributes->Flags;
return 0;
}
// Selects supported process-group and signal spawn attributes.
__declspec(dllexport) int posix_spawnattr_setflags(posix_spawnattr_t* attributes, short flags) {
const uint32_t valid = POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK |
POSIX_SPAWN_SETSIGDEF;
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC ||
((uint32_t)(unsigned short)flags & ~valid) != 0)
return EINVAL;
attributes->Flags = (uint32_t)(unsigned short)flags;
return 0;
}
// Returns the requested process group for POSIX_SPAWN_SETPGROUP.
__declspec(dllexport) int posix_spawnattr_getpgroup(const posix_spawnattr_t* attributes,
pid_t* pgroup) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !pgroup)
return EINVAL;
*pgroup = attributes->ProcessGroup;
return 0;
}
// Sets the requested child process group; zero means the child's own stable POSIX PID.
__declspec(dllexport) int posix_spawnattr_setpgroup(posix_spawnattr_t* attributes, pid_t pgroup) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || pgroup < 0)
return EINVAL;
attributes->ProcessGroup = pgroup;
return 0;
}
// Returns the explicit child signal mask stored in one spawn attribute object.
__declspec(dllexport) int posix_spawnattr_getsigmask(const posix_spawnattr_t* attributes,
sigset_t* mask) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask)
return EINVAL;
*mask = attributes->SignalMask;
return 0;
}
// Stores the child signal mask used with POSIX_SPAWN_SETSIGMASK.
__declspec(dllexport) int posix_spawnattr_setsigmask(posix_spawnattr_t* attributes,
const sigset_t* mask) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !mask ||
(*mask & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return EINVAL;
attributes->SignalMask = *mask & ~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
return 0;
}
// Returns the set forced to default disposition in the spawned image.
__declspec(dllexport) int posix_spawnattr_getsigdefault(const posix_spawnattr_t* attributes,
sigset_t* set) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set)
return EINVAL;
*set = attributes->SignalDefault;
return 0;
}
// Stores the signals forced to default disposition in the spawned image.
__declspec(dllexport) int posix_spawnattr_setsigdefault(posix_spawnattr_t* attributes,
const sigset_t* set) {
if (!attributes || attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC || !set ||
(*set & ~NTL_POSIX_SIGNAL_VALID_MASK) != 0)
return EINVAL;
attributes->SignalDefault = *set;
return 0;
}
// Constructs one POSIX child through the authoritative posixsrv transaction.
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
const char* path,
const posix_spawn_file_actions_t* fileActions,
const posix_spawnattr_t* attributes,
char* const argv[],
char* const envp[]) {
if (!pidOut || !path || !path[0]) return EINVAL;
if (fileActions && fileActions->Initialized != POSIX_SPAWN_ACTIONS_MAGIC) return EINVAL;
if (attributes && attributes->Initialized != POSIX_SPAWN_ATTRIBUTES_MAGIC) return EINVAL;
char* const* environment = envp ? envp : environ;
int error = PosixSpawnValidateInput(path, fileActions, argv, (char* const*)environment);
if (error) return error;
*pidOut = (pid_t)-1;
uint32_t transactionId = 0;
error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_SPAWN, &transactionId);
if (error) return error;
error = PosixConstructionSendExecutable(transactionId, path);
if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT, argv);
if (!error) error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT,
(char* const*)environment);
if (!error) error = PosixSpawnSendFileActions(transactionId, fileActions);
if (!error) error = PosixSpawnSendAttributes(transactionId, attributes);
if (error) {
PosixConstructionCancelTransaction(transactionId);
return error;
}
uint64_t inheritanceToken = 0;
KSTATUS status = PosixFdCreateInheritanceSnapshot(0, &inheritanceToken);
if (!KSUCCESS(status)) {
PosixConstructionCancelTransaction(transactionId);
return PosixStatusToErrno(status);
}
SYS_HANDLE securityContext = SYS_HANDLE_INVALID;
status = PosixProcessDeriveSecurityContext(&securityContext);
if (!KSUCCESS(status)) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
PosixConstructionCancelTransaction(transactionId);
return PosixStatusToErrno(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_SPAWN_COMMIT;
request.TransactionId = transactionId;
request.FdInheritanceToken = inheritanceToken;
SYS_PORT_HANDLE_TRANSFER transfer;
memset(&transfer, 0, sizeof(transfer));
transfer.Handle = securityContext;
transfer.DesiredRights = SYS_RIGHT_QUERY | SYS_SECURITY_CONTEXT_RIGHT_ASSIGN;
transfer.Flags = SYS_PORT_TRANSFER_FLAG_NONE;
NTL_POSIX_REPLY reply;
status = PosixRuntimeCallServerWithHandles(&request, &transfer, 1u, &reply);
(void)SysCloseHandle(securityContext);
if (!KSUCCESS(status)) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
PosixConstructionCancelTransaction(transactionId);
return PosixStatusToErrno(status);
}
if (!KSUCCESS(reply.Status)) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
PosixConstructionCancelTransaction(transactionId);
return PosixStatusToErrno(reply.Status);
}
if (reply.SpawnedPosixPid == 0 || reply.SpawnedPosixPid > INT32_MAX) return EIO;
*pidOut = (pid_t)reply.SpawnedPosixPid;
return 0;
}
// Validates one exec image and its replacement argv/envp payload.
static int PosixExecValidateInput(const char* path, char* const argv[], char* const envp[]) {
if (!path || !path[0]) return EINVAL;
size_t pathLength = strlen(path);
if (pathLength > SYS_PATH_MAX) return ENAMETOOLONG;
uint32_t totalBytes = 0;
int error = PosixConstructionMeasureString(path, &totalBytes);
if (!error) error = PosixConstructionMeasureVector(argv, &totalBytes);
if (!error) error = PosixConstructionMeasureVector(envp, &totalBytes);
return error;
}
// Replaces the current native generation while preserving its stable POSIX process identity.
__declspec(dllexport) int execve(const char* path, char* const argv[], char* const envp[]) {
int error = PosixExecValidateInput(path, argv, envp);
if (error) {
errno = error;
return -1;
}
POSIX_THREAD_STATE* thread = PosixRuntimeThreadState();
if (!thread || !thread->LogicalProcess) {
errno = EIO;
return -1;
}
POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild();
uint32_t transactionId = 0;
error = PosixConstructionBegin(NTL_POSIX_TRANSACTION_EXEC, &transactionId);
if (error) {
errno = error;
return -1;
}
error = PosixConstructionSendExecutable(transactionId, path);
if (!error)
error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ARGUMENT,
argv);
if (!error)
error = PosixConstructionSendVector(transactionId, NTL_POSIX_CONSTRUCT_VECTOR_ENVIRONMENT,
envp);
if (error) {
PosixConstructionCancelTransaction(transactionId);
errno = error;
return -1;
}
uint64_t inheritanceToken = 0;
KSTATUS status = PosixFdCreateInheritanceSnapshot(POSIX_FD_INHERIT_SKIP_CLOEXEC,
&inheritanceToken);
if (!KSUCCESS(status)) {
PosixConstructionCancelTransaction(transactionId);
errno = PosixStatusToErrno(status);
return -1;
}
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_EXEC_COMMIT;
request.TransactionId = transactionId;
request.FdInheritanceToken = inheritanceToken;
request.SignalMask = (thread->SignalBlocked | thread->SignalHandlerBlocked) &
~NTL_POSIX_SIGNAL_UNBLOCKABLE_MASK;
NTL_POSIX_REPLY reply;
status = PosixRuntimeCallServerBlockingWithHandles(
&request, (const SYS_PORT_HANDLE_TRANSFER*)0, 0, &reply);
if (!KSUCCESS(status)) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
PosixConstructionCancelTransaction(transactionId);
errno = PosixStatusToErrno(status);
return -1;
}
if (!KSUCCESS(reply.Status)) {
PosixFdCancelInheritanceSnapshot(inheritanceToken);
PosixConstructionCancelTransaction(transactionId);
errno = PosixStatusToErrno(reply.Status);
return -1;
}
if (borrowedChild) {
// The server has a READY replacement but will release it only after this thread is dead.
PosixVforkDetachBorrowedChildLocal(borrowedChild, false);
for (;;) SysExitThread(0);
}
// Ordinary successful exec has no reply path; reaching here violates that contract.
errno = EIO;
return -1;
}
// Returns the server-owned parent launch association for the current POSIX process.
__declspec(dllexport) pid_t getppid(void) {
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeGetProcessAttributes(&reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (pid_t)-1;
}
return (pid_t)reply.ParentPosixPid;
}
// Waits for one matching POSIX child record and collects it exactly once.
__declspec(dllexport) pid_t waitpid(pid_t pid, int* statusOut, int options) {
if ((options & ~WNOHANG) != 0) {
errno = EINVAL;
return (pid_t)-1;
}
NTL_POSIX_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Version = NTL_POSIX_PROTOCOL_VERSION;
request.Operation = NTL_POSIX_OP_WAIT_PROCESS;
request.WaitPid = (int32_t)pid;
request.WaitOptions = (options & WNOHANG) ? NTL_POSIX_WAIT_NOHANG : 0u;
NTL_POSIX_REPLY reply;
KSTATUS status = PosixRuntimeCallServerBlocking(&request, &reply);
if (!KSUCCESS(status)) {
errno = PosixStatusToErrno(status);
return (pid_t)-1;
}
if (!KSUCCESS(reply.Status)) {
errno = PosixStatusToErrno(reply.Status);
return (pid_t)-1;
}
if (reply.WaitedPosixPid == 0) return 0;
if (reply.WaitedPosixPid < 0) {
errno = EIO;
return (pid_t)-1;
}
if (statusOut) *statusOut = (reply.ExitCode & 0xff) << 8;
return (pid_t)reply.WaitedPosixPid;
}
// Waits for any POSIX child.
__declspec(dllexport) pid_t wait(int* status) {
return waitpid((pid_t)-1, status, 0);
}
// Terminates either an ordinary POSIX native generation or one borrowed vfork child.
__declspec(dllexport) void _exit(int status) {
POSIX_VFORK_CHILD_STATE* borrowedChild = PosixVforkCurrentBorrowedChild();
if (borrowedChild) {
(void)PosixVforkRecordExit(borrowedChild, status);
PosixVforkDetachBorrowedChildLocal(borrowedChild, true);
for (;;) SysExitThread(status);
}
for (;;) SysExitProcess(status);
}
// Terminates the process; stdio/atexit policy can layer above this when those facilities exist.
__declspec(dllexport) void exit(int status) {
SysExitProcess(status);
while (true) {}
_exit(status);
}
// Initializes one ordinary shared-attribute request.