From: Rose Hellsing <rose@pinkro.se> --- dlls/ntdll/ntdll.spec | 4 +- dlls/ntdll/signal_arm64ec.c | 1 + dlls/ntdll/unix/sync.c | 194 +++++++++++++++++++-- dlls/wow64/sync.c | 12 ++ server/lpc_port.c | 335 +++++++++++++++++++++++++++++++++++- server/protocol.def | 35 ++++ server/thread.c | 5 + server/thread.h | 1 + 8 files changed, 564 insertions(+), 23 deletions(-) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index 5aa620bd60b..0ee1b0303a0 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -370,7 +370,7 @@ @ stdcall -syscall=0x000b NtReplyWaitReceivePort(ptr ptr ptr ptr) @ stdcall -syscall=0x002b NtReplyWaitReceivePortEx(long ptr ptr ptr ptr) # @ stub NtReplyWaitReplyPort -# @ stub NtRequestPort +@ stdcall -syscall NtRequestPort(ptr ptr) @ stdcall -syscall=0x0022 NtRequestWaitReplyPort(ptr ptr ptr) @ stdcall -syscall NtResetEvent(long ptr) @ stdcall -syscall NtResetWriteWatch(long ptr long) @@ -1442,7 +1442,7 @@ @ stdcall -private ZwReplyWaitReceivePort(ptr ptr ptr ptr) NtReplyWaitReceivePort @ stdcall -private ZwReplyWaitReceivePortEx(long ptr ptr ptr ptr) NtReplyWaitReceivePortEx # @ stub ZwReplyWaitReplyPort -# @ stub ZwRequestPort +@ stdcall -private ZwRequestPort(ptr ptr) NtRequestPort @ stdcall -private ZwRequestWaitReplyPort(ptr ptr ptr) NtRequestWaitReplyPort @ stdcall -private ZwResetEvent(long ptr) NtResetEvent @ stdcall -private ZwResetWriteWatch(long ptr long) NtResetWriteWatch diff --git a/dlls/ntdll/signal_arm64ec.c b/dlls/ntdll/signal_arm64ec.c index 517f7690bf1..dd139721966 100644 --- a/dlls/ntdll/signal_arm64ec.c +++ b/dlls/ntdll/signal_arm64ec.c @@ -532,6 +532,7 @@ DEFINE_SYSCALL(NtReplaceKey, (OBJECT_ATTRIBUTES *attr, HANDLE key, OBJECT_ATTRIB DEFINE_SYSCALL(NtReplyPort, (HANDLE handle, LPC_MESSAGE *reply)) DEFINE_SYSCALL(NtReplyWaitReceivePort, (HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg)) DEFINE_SYSCALL(NtReplyWaitReceivePortEx, (HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg, LARGE_INTEGER *timeout)) +DEFINE_SYSCALL(NtRequestPort, (HANDLE handle, LPC_MESSAGE *msg)) DEFINE_SYSCALL(NtRequestWaitReplyPort, (HANDLE handle, LPC_MESSAGE *msg_in, LPC_MESSAGE *msg_out)) DEFINE_SYSCALL(NtResetEvent, (HANDLE handle, LONG *prev_state)) DEFINE_SYSCALL(NtResetWriteWatch, (HANDLE process, PVOID base, SIZE_T size)) diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index 3179698202e..48a564545d9 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -3352,8 +3352,45 @@ NTSTATUS WINAPI NtReadRequestData( HANDLE handle, LPC_MESSAGE *request, ULONG id */ NTSTATUS WINAPI NtRegisterThreadTerminatePort( HANDLE handle ) { - FIXME( "(%p),stub!\n", handle ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p)\n", handle ); + + SERVER_START_REQ( register_lpc_terminate_port ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + return ret; +} + + +/*********************************************************************** + * NtRequestPort (NTDLL.@) + */ +NTSTATUS WINAPI NtRequestPort( HANDLE handle, LPC_MESSAGE *msg ) +{ + unsigned int ret; + USHORT data_size; + + TRACE( "(%p,%p)\n", handle, msg ); + + if (!msg) + return STATUS_INVALID_PARAMETER; + + data_size = msg->DataSize; + + SERVER_START_REQ( request_lpc_reply ) + { + req->handle = wine_server_obj_handle( handle ); + req->data_size = data_size; + req->msg_type = 3; /* datagram */ + wine_server_add_data( req, msg->Data, data_size ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + return ret; } @@ -3362,44 +3399,165 @@ NTSTATUS WINAPI NtRegisterThreadTerminatePort( HANDLE handle ) */ NTSTATUS WINAPI NtRequestWaitReplyPort( HANDLE handle, LPC_MESSAGE *msg_in, LPC_MESSAGE *msg_out ) { - FIXME( "(%p,%p,%p),stub!\n", handle, msg_in, msg_out ); - if (msg_in) - TRACE("datasize %u msgsize %u type %u ranges %u client %p/%p msgid %lu size %lu data %s\n", - msg_in->DataSize, msg_in->MessageSize, msg_in->MessageType, msg_in->VirtualRangesOffset, - msg_in->ClientId.UniqueProcess, msg_in->ClientId.UniqueThread, msg_in->MessageId, - msg_in->SectionSize, debugstr_an( (const char *)msg_in->Data, msg_in->DataSize )); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + USHORT data_size; + + TRACE( "(%p,%p,%p)\n", handle, msg_in, msg_out ); + + if (!msg_in || !msg_out) return STATUS_INVALID_PARAMETER; + + data_size = msg_in->DataSize; + + /* Send the request message */ + SERVER_START_REQ( request_lpc_reply ) + { + req->handle = wine_server_obj_handle( handle ); + req->data_size = data_size; + req->msg_type = 1; /* request */ + wine_server_add_data( req, msg_in->Data, data_size ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + if (ret) return ret; + + /* Wait for and receive the reply */ + for (;;) + { + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = 0; + req->reply_size = 0; + req->timeout = TIMEOUT_INFINITE; + /* Use a reasonable max size for LPC message data. + * sizeof(msg_out->Data) is just 1 due to ANYSIZE_ARRAY. */ + wine_server_set_reply( req, msg_out->Data, 0x1000 ); + ret = wine_server_call( req ); + if (!ret) + { + msg_out->DataSize = reply->data_size; + msg_out->MessageSize = sizeof(*msg_out) + reply->data_size; + msg_out->MessageType = reply->msg_type; + msg_out->VirtualRangesOffset = 0; + msg_out->ClientId.UniqueProcess = ULongToHandle( reply->client_pid ); + msg_out->ClientId.UniqueThread = ULongToHandle( reply->client_tid ); + msg_out->MessageId = reply->msg_id; + msg_out->SectionSize = 0; + } + } + SERVER_END_REQ; + + if (ret != STATUS_PENDING) break; + + /* Wait on port for message availability */ + ret = NtWaitForSingleObject( handle, FALSE, NULL ); + if (ret) break; + } + return ret; } /*********************************************************************** * NtReplyPort (NTDLL.@) */ -NTSTATUS WINAPI NtReplyPort( HANDLE handle, LPC_MESSAGE *reply ) +NTSTATUS WINAPI NtReplyPort( HANDLE handle, LPC_MESSAGE *reply_msg ) { - FIXME("(%p,%p),stub!\n", handle, reply ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + + TRACE( "(%p,%p)\n", handle, reply_msg ); + + if (!reply_msg) + return STATUS_INVALID_PARAMETER; + + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = reply_msg->MessageId; + req->reply_size = reply_msg->DataSize; + req->timeout = 0; /* Don't wait for a new message */ + wine_server_add_data( req, reply_msg->Data, reply_msg->DataSize ); + ret = wine_server_call( req ); + /* STATUS_PENDING just means no new message, which is fine for NtReplyPort */ + if (ret == STATUS_PENDING) + ret = STATUS_SUCCESS; + } + SERVER_END_REQ; + return ret; } /*********************************************************************** * NtReplyWaitReceivePort (NTDLL.@) */ -NTSTATUS WINAPI NtReplyWaitReceivePort( HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg ) +NTSTATUS WINAPI NtReplyWaitReceivePort( HANDLE handle, ULONG *id, LPC_MESSAGE *reply_msg, LPC_MESSAGE *msg ) { - FIXME("(%p,%p,%p,%p),stub!\n", handle, id, reply, msg ); - return STATUS_NOT_IMPLEMENTED; + return NtReplyWaitReceivePortEx( handle, id, reply_msg, msg, NULL ); } /*********************************************************************** * NtReplyWaitReceivePortEx (NTDLL.@) */ -NTSTATUS WINAPI NtReplyWaitReceivePortEx( HANDLE handle, ULONG *id, LPC_MESSAGE *reply, LPC_MESSAGE *msg, +NTSTATUS WINAPI NtReplyWaitReceivePortEx( HANDLE handle, ULONG *id, LPC_MESSAGE *reply_msg, LPC_MESSAGE *msg, LARGE_INTEGER *timeout ) { - FIXME("(%p,%p,%p,%p,%p),stub!\n", handle, id, reply, msg, timeout ); - return STATUS_NOT_IMPLEMENTED; + unsigned int ret; + timeout_t abs_timeout = timeout ? timeout->QuadPart : TIMEOUT_INFINITE; + unsigned int reply_msg_id = 0; + USHORT reply_size = 0; + + TRACE( "(%p,%p,%p,%p,%p)\n", handle, id, reply_msg, msg, timeout ); + + if (reply_msg) + { + reply_msg_id = reply_msg->MessageId; + reply_size = reply_msg->DataSize; + } + + for (;;) + { + SERVER_START_REQ( reply_wait_receive_lpc ) + { + req->handle = wine_server_obj_handle( handle ); + req->reply_msg_id = reply_msg_id; + req->reply_size = reply_size; + req->timeout = abs_timeout; + if (reply_msg && reply_size) + wine_server_add_data( req, reply_msg->Data, reply_size ); + if (msg) + { + /* Use a reasonable max size for LPC message data. + * sizeof(msg->Data) is just 1 due to ANYSIZE_ARRAY. */ + wine_server_set_reply( req, msg->Data, 0x1000 ); + } + ret = wine_server_call( req ); + if (!ret && msg) + { + msg->DataSize = reply->data_size; + msg->MessageSize = sizeof(*msg) + reply->data_size; + msg->MessageType = reply->msg_type; + msg->VirtualRangesOffset = 0; + msg->ClientId.UniqueProcess = ULongToHandle( reply->client_pid ); + msg->ClientId.UniqueThread = ULongToHandle( reply->client_tid ); + msg->MessageId = reply->msg_id; + msg->SectionSize = 0; + if (id) *id = (ULONG)(ULONG_PTR)reply->context; + } + } + SERVER_END_REQ; + + /* After first iteration, don't send reply again */ + reply_msg_id = 0; + reply_size = 0; + + if (ret != STATUS_PENDING) break; + + /* Wait on port for message availability */ + ret = NtWaitForSingleObject( handle, FALSE, timeout ); + if (ret) break; + } + return ret; } diff --git a/dlls/wow64/sync.c b/dlls/wow64/sync.c index dd2ff72e9ed..b083d63253e 100644 --- a/dlls/wow64/sync.c +++ b/dlls/wow64/sync.c @@ -1352,6 +1352,18 @@ NTSTATUS WINAPI wow64_NtReplyWaitReceivePortEx( UINT *args ) } +/********************************************************************** + * wow64_NtRequestPort + */ +NTSTATUS WINAPI wow64_NtRequestPort( UINT *args ) +{ + HANDLE handle = get_handle( &args ); + LPC_MESSAGE *msg = get_ptr( &args ); + + return NtRequestPort( handle, msg ); +} + + /********************************************************************** * wow64_NtRequestWaitReplyPort */ diff --git a/server/lpc_port.c b/server/lpc_port.c index cc13de09da7..ffb39e21cbf 100644 --- a/server/lpc_port.c +++ b/server/lpc_port.c @@ -51,6 +51,10 @@ #define PORT_FLAG_WAITABLE 0x0001 /* LPC message types */ +#define LPC_REQUEST 1 +#define LPC_REPLY 2 +#define LPC_DATAGRAM 3 +#define LPC_CLIENT_DIED 6 #define LPC_CONNECTION_REQUEST 10 /* Maximum message size */ @@ -63,6 +67,24 @@ static unsigned int global_msg_id_counter = 0; /* Global list of pending connection requests */ static struct list global_pending_connects = LIST_INIT(global_pending_connects); +/* Global list of pending requests awaiting replies */ +static struct list global_pending_requests = LIST_INIT(global_pending_requests); + +/* Pending request entry */ +struct pending_request +{ + struct list entry; /* entry in global_pending_requests */ + unsigned int msg_id; /* message ID waiting for reply */ + struct lpc_port *client_port; /* client port to deliver reply to */ +}; + +/* Entry in thread's list of ports to notify on termination */ +struct lpc_terminate_port_entry +{ + struct list entry; /* entry in thread's lpc_terminate_ports list */ + struct lpc_port *port; /* the LPC port (client port) */ +}; + static const WCHAR lpc_port_name[] = {'L','P','C',' ','P','o','r','t'}; struct type_descr lpc_port_type = @@ -89,6 +111,7 @@ struct lpc_message unsigned int msg_type; /* LPC message type */ process_id_t client_pid; /* sender's process ID */ thread_id_t client_tid; /* sender's thread ID */ + client_ptr_t port_context; /* port context for this message */ data_size_t data_size; /* size of message data */ char data[1]; /* variable-length message data */ }; @@ -101,12 +124,14 @@ struct lpc_port unsigned int flags; /* PORT_FLAG_* */ struct lpc_port *connection_port; /* reference to connection port */ struct lpc_port *connected_port; /* paired port: client <-> channel */ + struct list msg_queue; /* list of pending messages */ struct list pending_connects;/* list of pending connection messages */ struct object *queue_event; /* event signaled when message arrives */ unsigned int max_msg_len; /* maximum message length */ unsigned int max_connect_info;/* maximum connection info length */ struct object *wait_event; /* event for WaitForSingleObject (waitable ports) */ struct process *server_process; /* server process (for named ports) */ + client_ptr_t port_context; /* user-defined port context */ struct thread *client_thread; /* client thread (for NtCompleteConnectPort) */ struct object *connect_event; /* event signaled when connection completes */ unsigned int connect_status; /* STATUS_SUCCESS or error code from accept */ @@ -196,7 +221,7 @@ static void signal_port_queue( struct lpc_port *port ) /* Reset the port's queue event after receiving a message */ static void reset_port_queue( struct lpc_port *port ) { - if (list_empty( &port->pending_connects )) + if (list_empty( &port->msg_queue ) && list_empty( &port->pending_connects )) { if (port->queue_event) reset_sync( port->queue_event ); @@ -205,6 +230,36 @@ static void reset_port_queue( struct lpc_port *port ) } } +/* Track a pending request awaiting reply */ +static void track_pending_request( unsigned int msg_id, struct lpc_port *client_port ) +{ + struct pending_request *pr = mem_alloc( sizeof(*pr) ); + if (pr) + { + pr->msg_id = msg_id; + pr->client_port = (struct lpc_port *)grab_object( client_port ); + list_add_tail( &global_pending_requests, &pr->entry ); + } +} + +/* Find and remove a pending request by message ID */ +static struct lpc_port *find_pending_request_client( unsigned int msg_id ) +{ + struct pending_request *pr; + + LIST_FOR_EACH_ENTRY( pr, &global_pending_requests, struct pending_request, entry ) + { + if (pr->msg_id == msg_id) + { + struct lpc_port *client = pr->client_port; + list_remove( &pr->entry ); + free( pr ); + return client; + } + } + return NULL; +} + static struct lpc_port *create_lpc_port( struct object *root, const struct unicode_str *name, unsigned int attr, unsigned int flags, unsigned int max_msg_len, unsigned int max_connect_info, @@ -225,12 +280,14 @@ static struct lpc_port *create_lpc_port( struct object *root, const struct unico port->flags = flags; port->connection_port = (struct lpc_port *)grab_object( port ); port->connected_port = NULL; + list_init( &port->msg_queue ); list_init( &port->pending_connects ); port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = max_msg_len ? max_msg_len : MAX_LPC_MESSAGE_SIZE; port->max_connect_info = max_connect_info ? max_connect_info : 256; port->wait_event = NULL; port->server_process = (struct process *)grab_object( current->process ); + port->port_context = 0; port->client_thread = NULL; port->connect_event = NULL; port->connect_status = STATUS_PENDING; @@ -267,12 +324,14 @@ static struct lpc_port *create_port_internal( unsigned int port_type, struct lpc port->flags = 0; port->connection_port = (struct lpc_port *)grab_object( connection_port ); port->connected_port = NULL; + list_init( &port->msg_queue ); list_init( &port->pending_connects ); port->queue_event = create_internal_sync( 0, 0 ); port->max_msg_len = connection_port->max_msg_len; port->max_connect_info = connection_port->max_connect_info; port->wait_event = NULL; port->server_process = NULL; + port->port_context = 0; port->client_thread = NULL; port->connect_event = NULL; port->connect_status = STATUS_PENDING; @@ -325,6 +384,10 @@ static struct object *lpc_port_get_sync( struct object *obj ) struct lpc_port *port = (struct lpc_port *)obj; assert( obj->ops == &lpc_port_ops ); + /* For clients with a connect_event, always return it so wait entries + * added to connect_event's queue are properly checked when signaled. + * This is needed because accept_lpc_connect sets connect_status before + * complete_lpc_connect signals the event. */ if (port->port_type == PORT_TYPE_CLIENT && port->connect_event) return grab_object( port->connect_event ); @@ -344,6 +407,12 @@ static void lpc_port_destroy( struct object *obj ) assert( obj->ops == &lpc_port_ops ); + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->msg_queue, struct lpc_message, entry ) + { + list_remove( &msg->entry ); + free_lpc_message( msg ); + } + LIST_FOR_EACH_ENTRY_SAFE( msg, next_msg, &port->pending_connects, struct lpc_message, entry ) { list_remove( &msg->entry ); @@ -531,6 +600,7 @@ DECL_HANDLER(accept_lpc_connect) comm_port->connected_port = (struct lpc_port *)grab_object( client_port ); client_port->connected_port = (struct lpc_port *)grab_object( comm_port ); client_port->connect_status = STATUS_SUCCESS; + comm_port->port_context = req->context; if (msg->sender_thread) comm_port->client_thread = (struct thread *)grab_object( msg->sender_thread ); @@ -568,8 +638,18 @@ DECL_HANDLER(complete_lpc_connect) } client_port = port->connected_port; - if (client_port && client_port->connect_event) - signal_sync( client_port->connect_event ); + if (client_port) + { + /* Signal the connect_event to wake the client. The client is waiting + * on this event during NtConnectPort. After signaling, release the + * event so future waits will use queue_event instead. */ + if (client_port->connect_event) + { + signal_sync( client_port->connect_event ); + release_object( client_port->connect_event ); + client_port->connect_event = NULL; + } + } if (port->client_thread) { @@ -599,3 +679,252 @@ DECL_HANDLER(get_lpc_connect_status) reply->status = port->connect_status; release_object( port ); } + +/* Send a request message */ +DECL_HANDLER(request_lpc_reply) +{ + struct lpc_port *port; + struct lpc_port *target_port; + struct lpc_message *msg; + data_size_t data_size = get_req_data_size(); + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + if (port->port_type == PORT_TYPE_CLIENT) + { + if (!port->connected_port || !port->connected_port->connection_port) + { + set_error( STATUS_PORT_DISCONNECTED ); + release_object( port ); + return; + } + target_port = port->connected_port->connection_port; + } + else if (port->port_type == PORT_TYPE_CHANNEL) + { + if (!port->connected_port) + { + set_error( STATUS_PORT_DISCONNECTED ); + release_object( port ); + return; + } + target_port = port->connected_port; + } + else + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + msg = alloc_lpc_message( data_size ); + if (!msg) + { + release_object( port ); + return; + } + + msg->sender_port = (struct lpc_port *)grab_object( port ); + msg->sender_thread = (struct thread *)grab_object( current ); + msg->msg_id = get_next_msg_id(); + msg->msg_type = req->msg_type ? req->msg_type : LPC_REQUEST; + msg->client_pid = current->process->id; + msg->client_tid = current->id; + msg->port_context = port->port_context; + if (data_size) + memcpy( msg->data, get_req_data(), data_size ); + + if (msg->msg_type == LPC_REQUEST && port->port_type == PORT_TYPE_CLIENT) + track_pending_request( msg->msg_id, port ); + + list_add_tail( &target_port->msg_queue, &msg->entry ); + signal_port_queue( target_port ); + + reply->msg_id = msg->msg_id; + + release_object( port ); +} + +/* Reply to a message and wait for next one */ +DECL_HANDLER(reply_wait_receive_lpc) +{ + struct lpc_port *port; + struct lpc_port *receive_port; + struct lpc_message *msg; + data_size_t reply_size = get_req_data_size(); + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + PORT_CONNECT, &lpc_port_ops ); + if (!port) return; + + /* Handle reply to previous message */ + if (req->reply_msg_id) + { + struct lpc_port *client_port; + struct lpc_message *reply_msg; + + client_port = find_pending_request_client( req->reply_msg_id ); + if (client_port) + { + reply_msg = alloc_lpc_message( reply_size ); + if (reply_msg) + { + reply_msg->msg_type = LPC_REPLY; + reply_msg->msg_id = req->reply_msg_id; + reply_msg->client_pid = current->process->id; + reply_msg->client_tid = current->id; + reply_msg->data_size = reply_size; + if (reply_size) + memcpy( reply_msg->data, get_req_data(), reply_size ); + + list_add_tail( &client_port->msg_queue, &reply_msg->entry ); + signal_port_queue( client_port ); + } + release_object( client_port ); + } + } + + if (port->port_type == PORT_TYPE_CHANNEL || port->port_type == PORT_TYPE_CLIENT) + receive_port = port; + else if (port->port_type == PORT_TYPE_SERVER) + receive_port = port; + else + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + if (list_empty( &receive_port->msg_queue )) + { + if (port->port_type == PORT_TYPE_SERVER && !list_empty( &port->pending_connects )) + { + msg = LIST_ENTRY( list_head( &port->pending_connects ), struct lpc_message, entry ); + list_remove( &msg->entry ); + + reply->msg_id = msg->msg_id; + reply->msg_type = msg->msg_type; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->context = msg->port_context; + reply->data_size = msg->data_size; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + reset_port_queue( receive_port ); + release_object( port ); + return; + } + else + { + set_error( STATUS_PENDING ); + release_object( port ); + return; + } + } + else + { + msg = LIST_ENTRY( list_head( &receive_port->msg_queue ), struct lpc_message, entry ); + list_remove( &msg->entry ); + } + + reply->msg_id = msg->msg_id; + reply->msg_type = msg->msg_type; + reply->client_pid = msg->client_pid; + reply->client_tid = msg->client_tid; + reply->context = msg->port_context; + reply->data_size = msg->data_size; + + if (msg->data_size) + set_reply_data( msg->data, min( msg->data_size, get_reply_max_size() ) ); + + free_lpc_message( msg ); + reset_port_queue( receive_port ); + release_object( port ); +} + +/* Register a port for thread termination notification */ +DECL_HANDLER(register_lpc_terminate_port) +{ + struct lpc_port *port; + struct lpc_terminate_port_entry *entry; + + port = (struct lpc_port *)get_handle_obj( current->process, req->handle, + 0, &lpc_port_ops ); + if (!port) return; + + /* Only client ports should be registered for termination */ + if (port->port_type != PORT_TYPE_CLIENT) + { + set_error( STATUS_INVALID_PORT_HANDLE ); + release_object( port ); + return; + } + + /* Check if already registered */ + LIST_FOR_EACH_ENTRY( entry, ¤t->lpc_terminate_ports, struct lpc_terminate_port_entry, entry ) + { + if (entry->port == port) + { + release_object( port ); + return; + } + } + + /* Add to thread's terminate port list */ + entry = mem_alloc( sizeof(*entry) ); + if (entry) + { + entry->port = port; /* transfer the reference */ + list_add_tail( ¤t->lpc_terminate_ports, &entry->entry ); + } + else + { + release_object( port ); + } +} + +/* Send LPC_CLIENT_DIED messages to all registered ports for a thread. + * Called from cleanup_thread in thread.c */ +void lpc_send_client_died( struct thread *thread ) +{ + struct lpc_terminate_port_entry *entry, *next; + + LIST_FOR_EACH_ENTRY_SAFE( entry, next, &thread->lpc_terminate_ports, + struct lpc_terminate_port_entry, entry ) + { + struct lpc_port *client_port = entry->port; + + /* Send LPC_CLIENT_DIED to the server port via the communication channel */ + if (client_port && client_port->connected_port) + { + struct lpc_port *comm_port = client_port->connected_port; + struct lpc_port *server_port = comm_port->connection_port; + + if (server_port && server_port->port_type == PORT_TYPE_SERVER) + { + struct lpc_message *died_msg = alloc_lpc_message( 0 ); + if (died_msg) + { + died_msg->msg_id = get_next_msg_id(); + died_msg->msg_type = LPC_CLIENT_DIED; + died_msg->client_pid = thread->process->id; + died_msg->client_tid = thread->id; + died_msg->port_context = comm_port->port_context; + + /* Queue on server port */ + list_add_tail( &server_port->msg_queue, &died_msg->entry ); + signal_port_queue( server_port ); + } + } + } + + /* Clean up the entry */ + list_remove( &entry->entry ); + release_object( client_port ); + free( entry ); + } +} diff --git a/server/protocol.def b/server/protocol.def index 8116f197fe2..f8069d26e72 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -4338,3 +4338,38 @@ enum inproc_sync_type @REPLY unsigned int status; /* connection status */ @END + + +/* Send request and wait for reply */ +@REQ(request_lpc_reply) + obj_handle_t handle; + data_size_t data_size; + unsigned int msg_type; + VARARG(data,bytes); +@REPLY + unsigned int msg_id; +@END + + +/* Wait for message and reply to previous */ +@REQ(reply_wait_receive_lpc) + obj_handle_t handle; + unsigned int reply_msg_id; + data_size_t reply_size; + timeout_t timeout; + VARARG(reply,bytes); +@REPLY + unsigned int msg_id; + unsigned int msg_type; + process_id_t client_pid; + thread_id_t client_tid; + client_ptr_t context; + data_size_t data_size; + VARARG(data,bytes); +@END + + +/* Register port for thread termination notification */ +@REQ(register_lpc_terminate_port) + obj_handle_t handle; +@END diff --git a/server/thread.c b/server/thread.c index 7207f918400..e5280ac4315 100644 --- a/server/thread.c +++ b/server/thread.c @@ -60,6 +60,9 @@ #include "user.h" #include "security.h" +/* LPC support */ +extern void lpc_send_client_died( struct thread *thread ); + /* thread queues */ @@ -437,6 +440,7 @@ static inline void init_thread_structure( struct thread *thread ) list_init( &thread->system_apc ); list_init( &thread->user_apc ); list_init( &thread->kernel_object ); + list_init( &thread->lpc_terminate_ports ); for (i = 0; i < MAX_INFLIGHT_FDS; i++) thread->inflight[i].server = thread->inflight[i].client = -1; @@ -609,6 +613,7 @@ static void cleanup_thread( struct thread *thread ) { int i; + lpc_send_client_died( thread ); cleanup_thread_completion( thread ); if (thread->context) { diff --git a/server/thread.h b/server/thread.h index 77ea355483d..ca23822f572 100644 --- a/server/thread.h +++ b/server/thread.h @@ -99,6 +99,7 @@ struct thread data_size_t desc_len; /* thread description length in bytes */ WCHAR *desc; /* thread description string */ struct completion_wait *completion_wait; /* completion port wait object the thread is associated with */ + struct list lpc_terminate_ports; /* list of LPC ports to notify on thread termination */ }; extern struct thread *current; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10611