Files
nt-lite/code/user/system/rssrv/session.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

465 lines
20 KiB
C

// RSSRV interactive-session policy. Session 0 owns display routing, activation, focus, pointer capture, and DWM authority.
#include "rssrv_internal.h"
#include <ntlite/internal/session_control.h>
#include <ntlite/port.h>
#include <string.h>
#define RSSRV_SESSION_ZERO_PATH "/Sessions/0"
#define RSSRV_SESSION_ZERO_CONSOLE_PATH "/Sessions/0/Console"
#define RSSRV_CURRENT_CONSOLE_PATH "/Console"
#define RSSRV_DWM_LISTENER_BACKLOG 1u
// Acquires the session-state mutex.
static KSTATUS RssrvSessionLock(RSSRV_SESSION_STATE* session) {
if (!session || session->Mutex == SYS_HANDLE_INVALID) return NTLSTATUS_INVALID_PARAMETER;
return SysWait(session->Mutex, SYS_WAIT_FLAG_NONE);
}
// Releases the session-state mutex.
static void RssrvSessionUnlock(RSSRV_SESSION_STATE* session) {
if (session && session->Mutex != SYS_HANDLE_INVALID) (void)SysReleaseMutex(session->Mutex);
}
// Advances the session generation while preserving zero as an uninitialized value.
static void RssrvSessionChangedLocked(RSSRV_SESSION_STATE* session) {
session->Generation++;
if (session->Generation == 0) session->Generation = 1;
}
// Sends one best-effort DWM input notification while holding the session mutex.
static void RssrvSessionSendDwmEventLocked(RSSRV_SESSION_STATE* session,
uint32_t type,
const INPUT_KEY_RECORD* key,
const INPUT_MOUSE_EVENT* mouse,
int32_t x,
int32_t y) {
if (!session || session->DwmEndpoint == SYS_HANDLE_INVALID) return;
NTL_SESSION_DWM_EVENT event;
memset(&event, 0, sizeof(event));
event.Size = sizeof(event);
event.Version = NTL_SESSION_DWM_PROTOCOL_VERSION;
event.Type = type;
event.Flags = NTL_SESSION_DWM_FLAG_NONE;
event.PointerX = x;
event.PointerY = y;
if (key) event.Key = *key;
if (mouse) event.Mouse = *mouse;
SYS_SEND_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = session->DwmEndpoint;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &event;
send.PayloadLength = sizeof(event);
send.TimeoutMilliseconds = 0;
(void)SysSendPort(&send);
}
// Routes one focused keyboard record into the connected DWM desktop target.
static void RssrvSessionDwmRouteKey(void* context, const INPUT_KEY_RECORD* record) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)context;
if (!session || !record) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_DELIVER_KEY, record, 0, 0, 0);
RssrvSessionUnlock(session);
}
// Routes one focused/captured mouse record into the connected DWM desktop target.
static void RssrvSessionDwmRouteMouse(void* context,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)context;
if (!session || !event) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_DELIVER_MOUSE, 0, event, x, y);
RssrvSessionUnlock(session);
}
// Replies to one private DWM session-control request.
static void RssrvSessionReplyDwm(SYS_HANDLE endpoint,
uint32_t messageId,
uint32_t operation,
KSTATUS status) {
NTL_SESSION_DWM_REPLY reply;
memset(&reply, 0, sizeof(reply));
reply.Size = sizeof(reply);
reply.Version = NTL_SESSION_DWM_PROTOCOL_VERSION;
reply.Operation = operation;
reply.Flags = NTL_SESSION_DWM_FLAG_NONE;
reply.Status = status;
SYS_REPLY_PORT_REQUEST send;
memset(&send, 0, sizeof(send));
send.Size = sizeof(send);
send.EndpointHandle = endpoint;
send.MessageId = messageId;
send.Flags = SYS_PORT_FLAG_NONE;
send.Payload = &reply;
send.PayloadLength = sizeof(reply);
(void)SysReplyPort(&send);
}
// Applies one DWM focus/capture request while preserving RSSRV as the session authority owner.
static KSTATUS RssrvSessionApplyDwmRequest(RSSRV_SESSION_STATE* session,
const NTL_SESSION_DWM_REQUEST* request) {
if (!session || !request || request->Enabled > 1u) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
RSSRV_INPUT_TARGET* desktop = &session->DesktopTarget;
RSSRV_INPUT_TARGET* fallback = session->ActiveTarget == desktop ? (RSSRV_INPUT_TARGET*)0 : session->ActiveTarget;
switch (request->Operation) {
case NTL_SESSION_DWM_OP_SET_KEYBOARD_FOCUS:
if (request->Enabled) {
if (session->KeyboardFocus != desktop) {
session->KeyboardFocus = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->KeyboardFocus == desktop) {
session->KeyboardFocus = fallback;
RssrvSessionChangedLocked(session);
}
break;
case NTL_SESSION_DWM_OP_SET_POINTER_FOCUS:
if (request->Enabled) {
if (session->PointerFocus != desktop) {
session->PointerFocus = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->PointerFocus == desktop) {
session->PointerFocus = fallback;
RssrvSessionChangedLocked(session);
}
break;
case NTL_SESSION_DWM_OP_SET_POINTER_CAPTURE:
if (request->Enabled) {
if (session->PointerCapture != desktop) {
session->PointerCapture = desktop;
RssrvSessionChangedLocked(session);
}
} else if (session->PointerCapture == desktop) {
session->PointerCapture = (RSSRV_INPUT_TARGET*)0;
RssrvSessionChangedLocked(session);
}
break;
default:
status = NTLSTATUS_INVALID_PARAMETER;
break;
}
RssrvSessionUnlock(session);
return status;
}
// Restores non-DWM routing after one DWM control connection disappears.
static void RssrvSessionDisconnectDwm(RSSRV_SESSION_STATE* session, SYS_HANDLE endpoint) {
if (!session) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
if (session->DwmEndpoint == endpoint) {
RSSRV_INPUT_TARGET* desktop = &session->DesktopTarget;
RSSRV_INPUT_TARGET* fallback = session->ActiveTarget == desktop ? (RSSRV_INPUT_TARGET*)0 : session->ActiveTarget;
if (session->KeyboardFocus == desktop) session->KeyboardFocus = fallback;
if (session->PointerFocus == desktop) session->PointerFocus = fallback;
if (session->PointerCapture == desktop) session->PointerCapture = (RSSRV_INPUT_TARGET*)0;
session->DwmEndpoint = SYS_HANDLE_INVALID;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
}
// Accepts replaceable DWM connections and serves the private Session 0 authority protocol.
static void RssrvSessionDwmListenerThread(uintptr_t argument) {
RSSRV_SESSION_STATE* session = (RSSRV_SESSION_STATE*)argument;
RssrvSetCurrentThreadName("rssrv.session.dwm");
if (!session) SysExitThread(1);
for (;;) {
SYS_HANDLE endpoint = SYS_HANDLE_INVALID;
SYS_ACCEPT_PORT_REQUEST accept;
memset(&accept, 0, sizeof(accept));
accept.Size = sizeof(accept);
accept.ListenerHandle = session->DwmListener;
accept.Flags = SYS_PORT_ACCEPT_FLAG_NONE;
accept.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
accept.EndpointHandleOut = &endpoint;
KSTATUS status = SysAcceptPort(&accept);
if (!KSUCCESS(status)) RssrvExit(16);
status = RssrvSessionLock(session);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(endpoint);
continue;
}
if (session->DwmEndpoint != SYS_HANDLE_INVALID) {
RssrvSessionUnlock(session);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
continue;
}
session->DwmEndpoint = endpoint;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
for (;;) {
NTL_SESSION_DWM_REQUEST request;
SYS_PORT_MESSAGE_INFO info;
memset(&request, 0, sizeof(request));
memset(&info, 0, sizeof(info));
info.Size = sizeof(info);
SYS_RECEIVE_PORT_REQUEST receive;
memset(&receive, 0, sizeof(receive));
receive.Size = sizeof(receive);
receive.EndpointHandle = endpoint;
receive.Flags = SYS_PORT_FLAG_NONE;
receive.Payload = &request;
receive.PayloadCapacity = sizeof(request);
receive.InfoOut = &info;
receive.TimeoutMilliseconds = SYS_PORT_TIMEOUT_INFINITE;
status = SysReceivePort(&receive);
if (status == NTLSTATUS_PORT_DISCONNECTED || !KSUCCESS(status)) break;
if (info.Type != SYS_PORT_MESSAGE_REQUEST || info.HandleCount != 0 ||
info.PayloadLength != sizeof(request) || request.Size != sizeof(request) ||
request.Version != NTL_SESSION_DWM_PROTOCOL_VERSION ||
request.Flags != NTL_SESSION_DWM_FLAG_NONE || request.Reserved[0] != 0 ||
request.Reserved[1] != 0 || request.Reserved[2] != 0) {
RssrvSessionReplyDwm(endpoint, info.MessageId, request.Operation, NTLSTATUS_INVALID_PARAMETER);
continue;
}
status = RssrvSessionApplyDwmRequest(session, &request);
RssrvSessionReplyDwm(endpoint, info.MessageId, request.Operation, status);
}
RssrvSessionDisconnectDwm(session, endpoint);
(void)SysDisconnectPort(endpoint);
(void)SysCloseHandle(endpoint);
}
}
// Initializes Session 0 and creates the private listener that init may delegate only to DWM.
KSTATUS RssrvSessionInitialize(RSSRV_SESSION_STATE* session,
RSSRV_NAMESPACE* namespaceState,
RSSRV_DISPLAY_STATE* display) {
if (!session || !namespaceState || !display) return NTLSTATUS_INVALID_PARAMETER;
memset(session, 0, sizeof(*session));
session->Mutex = SYS_HANDLE_INVALID;
session->DwmListener = SYS_HANDLE_INVALID;
session->DwmEndpoint = SYS_HANDLE_INVALID;
session->Namespace = namespaceState;
session->Display = display;
session->Id = 0;
session->Generation = 1;
session->NextTargetId = 1;
SYS_DISPLAY_INFO info;
memset(&info, 0, sizeof(info));
KSTATUS status = RssrvDisplayQueryInfo(display, &info);
if (!KSUCCESS(status)) return status;
session->DisplayWidth = info.Mode.Width ? (int32_t)info.Mode.Width : 1;
session->DisplayHeight = info.Mode.Height ? (int32_t)info.Mode.Height : 1;
status = SysCreateMutex(&session->Mutex);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceCreateDirectory(namespaceState,
RSSRV_SESSION_ZERO_PATH,
sizeof(RSSRV_SESSION_ZERO_PATH) - 1u);
if (!KSUCCESS(status)) return status;
SYS_CREATE_PORT_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Flags = SYS_PORT_FLAG_NONE;
create.Backlog = RSSRV_DWM_LISTENER_BACKLOG;
create.HandleOut = &session->DwmListener;
return SysCreatePort(&create);
}
// Registers one stable in-process input target with Session 0.
KSTATUS RssrvSessionRegisterInputTarget(RSSRV_SESSION_STATE* session,
RSSRV_INPUT_TARGET* target,
uint32_t kind,
void* context,
RSSRV_INPUT_TARGET_KEY_ROUTINE keyRoutine,
RSSRV_INPUT_TARGET_MOUSE_ROUTINE mouseRoutine) {
if (!session || !target || kind == RSSRV_INPUT_TARGET_KIND_NONE || !context)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (target->Id != 0) {
RssrvSessionUnlock(session);
return NTLSTATUS_OBJECT_NAME_COLLISION;
}
memset(target, 0, sizeof(*target));
target->Id = session->NextTargetId++;
if (target->Id == 0) target->Id = session->NextTargetId++;
target->Kind = kind;
target->Session = session;
target->Context = context;
target->KeyRoutine = keyRoutine;
target->MouseRoutine = mouseRoutine;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Activates one registered target and gives it ordinary keyboard and pointer focus.
KSTATUS RssrvSessionActivateInputTarget(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || !target || target->Id == 0 || target->Session != session) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
session->ActiveTarget = target;
session->KeyboardFocus = target;
session->PointerFocus = target;
RssrvSessionChangedLocked(session);
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets keyboard focus without changing activation or pointer routing.
KSTATUS RssrvSessionSetKeyboardFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->KeyboardFocus != target) {
session->KeyboardFocus = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets ordinary pointer focus without disturbing an active pointer capture.
KSTATUS RssrvSessionSetPointerFocus(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->PointerFocus != target) {
session->PointerFocus = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Sets or releases pointer capture independently of ordinary pointer focus.
KSTATUS RssrvSessionSetPointerCapture(RSSRV_SESSION_STATE* session, RSSRV_INPUT_TARGET* target) {
if (!session || (target && (target->Id == 0 || target->Session != session))) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
if (session->PointerCapture != target) {
session->PointerCapture = target;
RssrvSessionChangedLocked(session);
}
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Returns the active session display bounds used to clamp the logical pointer.
KSTATUS RssrvSessionQueryDisplayBounds(RSSRV_SESSION_STATE* session, int32_t* widthOut, int32_t* heightOut) {
if (!session || !widthOut || !heightOut) return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionLock(session);
if (!KSUCCESS(status)) return status;
*widthOut = session->DisplayWidth;
*heightOut = session->DisplayHeight;
RssrvSessionUnlock(session);
return NTLSTATUS_SUCCESS;
}
// Gives DWM a non-consuming observation of one logical key before authoritative session routing occurs.
void RssrvSessionObserveKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record) {
if (!session || !record) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_OBSERVE_KEY, record, 0, 0, 0);
RssrvSessionUnlock(session);
}
// Gives DWM a non-consuming observation of one logical mouse event for hit testing and cursor policy.
void RssrvSessionObserveMouse(RSSRV_SESSION_STATE* session,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
if (!session || !event) return;
if (!KSUCCESS(RssrvSessionLock(session))) return;
RssrvSessionSendDwmEventLocked(session, NTL_SESSION_DWM_EVENT_OBSERVE_MOUSE, 0, event, x, y);
RssrvSessionUnlock(session);
}
// Routes one logical keyboard record to the current keyboard-focus target.
void RssrvSessionRouteKey(RSSRV_SESSION_STATE* session, const INPUT_KEY_RECORD* record) {
if (!session || !record) return;
RSSRV_INPUT_TARGET* target = (RSSRV_INPUT_TARGET*)0;
if (KSUCCESS(RssrvSessionLock(session))) {
target = session->KeyboardFocus;
RssrvSessionUnlock(session);
}
if (target && target->KeyRoutine) target->KeyRoutine(target->Context, record);
}
// Routes one logical mouse event through capture or pointer focus; DWM owns visible cursor policy.
void RssrvSessionRouteMouse(RSSRV_SESSION_STATE* session,
const INPUT_MOUSE_EVENT* event,
int32_t x,
int32_t y) {
if (!session || !event) return;
RSSRV_INPUT_TARGET* target = (RSSRV_INPUT_TARGET*)0;
if (KSUCCESS(RssrvSessionLock(session))) {
target = session->PointerCapture ? session->PointerCapture : session->PointerFocus;
RssrvSessionUnlock(session);
}
if (target && target->MouseRoutine) target->MouseRoutine(target->Context, event, x, y);
}
// Publishes Session 0's console alias and starts the private DWM authority listener.
KSTATUS RssrvSessionStart(RSSRV_SESSION_STATE* session, RSSRV_CONSOLE_STATE* console) {
if (!session || !console || console->Listener == SYS_HANDLE_INVALID || !session->ActiveTarget ||
session->DwmListener == SYS_HANDLE_INVALID)
return NTLSTATUS_INVALID_PARAMETER;
KSTATUS status = RssrvSessionRegisterInputTarget(session, &session->DesktopTarget,
RSSRV_INPUT_TARGET_KIND_DESKTOP, session,
RssrvSessionDwmRouteKey, RssrvSessionDwmRouteMouse);
if (!KSUCCESS(status)) return status;
SYS_HANDLE sessionConsole = SYS_HANDLE_INVALID;
SYS_DERIVE_HANDLE_REQUEST derive;
memset(&derive, 0, sizeof(derive));
derive.Size = sizeof(derive);
derive.SourceHandle = console->Listener;
derive.DesiredRights = SYS_RIGHT_TRANSFER | SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT;
derive.DerivedHandleOut = &sessionConsole;
status = SysDeriveHandle(&derive);
if (!KSUCCESS(status)) return status;
status = RssrvNamespaceBindOwned(session->Namespace,
RSSRV_SESSION_ZERO_CONSOLE_PATH,
sizeof(RSSRV_SESSION_ZERO_CONSOLE_PATH) - 1u,
NTL_RESOURCE_KIND_CAPABILITY,
sessionConsole,
SYS_RIGHT_QUERY | SYS_PORT_RIGHT_CONNECT);
if (!KSUCCESS(status)) {
(void)SysCloseHandle(sessionConsole);
return status;
}
status = RssrvNamespaceCreateSymbolicLink(session->Namespace,
RSSRV_CURRENT_CONSOLE_PATH,
sizeof(RSSRV_CURRENT_CONSOLE_PATH) - 1u,
RSSRV_SESSION_ZERO_CONSOLE_PATH,
sizeof(RSSRV_SESSION_ZERO_CONSOLE_PATH) - 1u);
if (!KSUCCESS(status)) return status;
SYS_CREATE_THREAD_REQUEST create;
memset(&create, 0, sizeof(create));
create.Size = sizeof(create);
create.Entry = (uintptr_t)RssrvSessionDwmListenerThread;
create.Argument = (uintptr_t)session;
create.Priority = 1u;
create.Flags = SYS_CREATE_THREAD_FLAG_NONE;
return SysCreateThread(&create);
}