From bf55457f24c12882a161cb583d3067142c941107 Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Thu, 23 Aug 2001 23:29:20 +0000 Subject: [PATCH] Implement WaitNamedPipe and DisconnectNamedPipe. Add a state for each pipe handle in the server. Create a socket on when the pipe is opened, not before. --- dlls/kernel/sync.c | 101 +++++++++++----- include/wine/server_protocol.h | 24 +++- server/named_pipe.c | 204 +++++++++++++++++++++++++-------- server/protocol.def | 14 +++ server/request.h | 4 + server/trace.c | 19 +++ 6 files changed, 290 insertions(+), 76 deletions(-) diff --git a/dlls/kernel/sync.c b/dlls/kernel/sync.c index 252fd40629d..6d3e95794c7 100644 --- a/dlls/kernel/sync.c +++ b/dlls/kernel/sync.c @@ -605,24 +605,73 @@ BOOL WINAPI PeekNamedPipe( HANDLE hPipe, LPVOID lpvBuffer, DWORD cbBuffer, /*********************************************************************** * WaitNamedPipeA (KERNEL32.@) */ -BOOL WINAPI WaitNamedPipeA (LPCSTR lpNamedPipeName, DWORD nTimeOut) +BOOL WINAPI WaitNamedPipeA (LPCSTR name, DWORD nTimeOut) { - FIXME("%s 0x%08lx\n",lpNamedPipeName,nTimeOut); - SetLastError(ERROR_PIPE_NOT_CONNECTED); - return FALSE; + DWORD len = name ? MultiByteToWideChar( CP_ACP, 0, name, strlen(name), NULL, 0 ) : 0; + HANDLE event; + BOOL ret; + + TRACE("%s 0x%08lx\n",debugstr_a(name),nTimeOut); + + if (len >= MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; + } + + if (!(event = CreateEventA( NULL, 0, 0, NULL ))) return FALSE; + + SERVER_START_VAR_REQ( wait_named_pipe, len * sizeof(WCHAR) ) + { + req->timeout = nTimeOut; + req->event = event; + if (len) MultiByteToWideChar( CP_ACP, 0, name, strlen(name), server_data_ptr(req), len ); + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + if (ret) WaitForSingleObject(event,INFINITE); + + CloseHandle(event); + return ret; } /*********************************************************************** * WaitNamedPipeW (KERNEL32.@) */ -BOOL WINAPI WaitNamedPipeW (LPCWSTR lpNamedPipeName, DWORD nTimeOut) +BOOL WINAPI WaitNamedPipeW (LPCWSTR name, DWORD nTimeOut) { - FIXME("%s 0x%08lx\n",debugstr_w(lpNamedPipeName),nTimeOut); - SetLastError(ERROR_PIPE_NOT_CONNECTED); - return FALSE; + DWORD len = name ? strlenW(name) : 0; + HANDLE event; + BOOL ret; + + TRACE("%s 0x%08lx\n",debugstr_w(name),nTimeOut); + + if (len >= MAX_PATH) + { + SetLastError( ERROR_FILENAME_EXCED_RANGE ); + return FALSE; + } + + if (!(event = CreateEventA( NULL, 0, 0, NULL ))) return FALSE; + + SERVER_START_VAR_REQ( wait_named_pipe, len * sizeof(WCHAR) ) + { + req->timeout = nTimeOut; + req->event = event; + memcpy( server_data_ptr(req), name, len * sizeof(WCHAR) ); + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + if (ret) WaitForSingleObject(event,INFINITE); + + CloseHandle(event); + return ret; } + /*********************************************************************** * ConnectNamedPipe (KERNEL32.@) */ @@ -631,7 +680,7 @@ BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped) BOOL ret; HANDLE event; - TRACE("(%d,%p):stub\n",hPipe, overlapped); + TRACE("(%d,%p)\n",hPipe, overlapped); if(overlapped) { @@ -640,29 +689,20 @@ BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped) return FALSE; } - event = CreateEventA(NULL,0,0,NULL); - if(event==INVALID_HANDLE_VALUE) - { - ERR("create event failed!\n"); - return FALSE; - } + if (!(event = CreateEventA(NULL,0,0,NULL))) return FALSE; SERVER_START_REQ( connect_named_pipe ) { req->handle = hPipe; req->event = event; - ret = SERVER_CALL_ERR(); + ret = !SERVER_CALL_ERR(); } SERVER_END_REQ; - if(ret) { - ERR("server returned status %08lx\n",GetLastError()); - return FALSE; - } + if (ret) WaitForSingleObject(event,INFINITE); - WaitForSingleObject(event,INFINITE); - - return TRUE; + CloseHandle(event); + return ret; } /*********************************************************************** @@ -670,8 +710,17 @@ BOOL WINAPI ConnectNamedPipe(HANDLE hPipe, LPOVERLAPPED overlapped) */ BOOL WINAPI DisconnectNamedPipe(HANDLE hPipe) { - FIXME("(%d):stub\n",hPipe); - SetLastError(ERROR_CALL_NOT_IMPLEMENTED); - return FALSE; + BOOL ret; + + TRACE("(%d)\n",hPipe); + + SERVER_START_REQ( disconnect_named_pipe ) + { + req->handle = hPipe; + ret = !SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + return ret; } diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index ebf2721de34..e62090c53da 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -1532,6 +1532,24 @@ struct connect_named_pipe_request }; + +struct wait_named_pipe_request +{ + struct request_header __header; + unsigned int timeout; + handle_t event; + /* VARARG(filename,string); */ +}; + + + +struct disconnect_named_pipe_request +{ + struct request_header __header; + handle_t handle; +}; + + enum request { REQ_new_process, @@ -1654,6 +1672,8 @@ enum request REQ_create_named_pipe, REQ_open_named_pipe, REQ_connect_named_pipe, + REQ_wait_named_pipe, + REQ_disconnect_named_pipe, REQ_NB_REQUESTS }; @@ -1781,8 +1801,10 @@ union generic_request struct create_named_pipe_request create_named_pipe; struct open_named_pipe_request open_named_pipe; struct connect_named_pipe_request connect_named_pipe; + struct wait_named_pipe_request wait_named_pipe; + struct disconnect_named_pipe_request disconnect_named_pipe; }; -#define SERVER_PROTOCOL_VERSION 49 +#define SERVER_PROTOCOL_VERSION 50 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/named_pipe.c b/server/named_pipe.c index b5fb4ab4232..bc068a5c9e7 100644 --- a/server/named_pipe.c +++ b/server/named_pipe.c @@ -2,6 +2,10 @@ * Server-side pipe management * * Copyright (C) 1998 Alexandre Julliard + * Copyright (C) 2001 Mike McCormack + * + * TODO: + * improve error handling */ #include "config.h" @@ -23,12 +27,24 @@ #include "thread.h" #include "request.h" +enum pipe_state +{ + ps_none, + ps_idle_server, + ps_wait_open, + ps_wait_connect, + ps_connected_server, + ps_connected_client, + ps_disconnected +}; + struct named_pipe; struct pipe_user { struct object obj; - int other_fd; + enum pipe_state state; + struct pipe_user *other; struct named_pipe *pipe; struct pipe_user *next; struct pipe_user *prev; @@ -96,8 +112,7 @@ static void pipe_user_dump( struct object *obj, int verbose ) { struct pipe_user *user = (struct pipe_user *)obj; assert( obj->ops == &pipe_user_ops ); - fprintf( stderr, "named pipe user %p (%s)\n", user, - (user->other_fd != -1) ? "server" : "client" ); + fprintf( stderr, "named pipe user %p (state %d)\n", user, user->state ); } static void named_pipe_destroy( struct object *obj) @@ -118,6 +133,25 @@ static void pipe_user_destroy( struct object *obj) release_object(user->event); user->event = NULL; } + if(user->other) + { + close(user->other->obj.fd); + user->other->obj.fd = -1; + switch(user->other->state) + { + case ps_connected_server: + user->other->state = ps_idle_server; + break; + case ps_connected_client: + user->other->state = ps_disconnected; + break; + default: + fprintf(stderr,"connected pipe has strange state %d!\n", + user->other->state); + } + user->other->other=NULL; + user->other = NULL; + } /* remove user from pipe's user list */ if (user->next) user->next->prev = user->prev; @@ -157,29 +191,15 @@ static struct pipe_user *get_pipe_user_obj( struct process *process, handle_t ha static struct pipe_user *create_pipe_user( struct named_pipe *pipe, int fd ) { struct pipe_user *user; - int fds[2]; - if(fd == -1) - { - /* FIXME: what about messages? */ - - if(0>socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) goto error; - } - else - { - if ((fds[0] = dup(fd)) == -1) goto error; - fds[1] = -1; - } - user = alloc_object( &pipe_user_ops, fds[0] ); + user = alloc_object( &pipe_user_ops, fd ); if(!user) - { - if (fds[1] != -1) close( fds[1] ); return NULL; - } user->pipe = pipe; - user->other_fd = fds[1]; + user->state = ps_none; user->event = NULL; /* thread wait on this pipe */ + user->other = NULL; /* add to list of pipe users */ if ((user->next = pipe->users)) user->next->prev = user; @@ -189,29 +209,21 @@ static struct pipe_user *create_pipe_user( struct named_pipe *pipe, int fd ) grab_object(pipe); return user; - - error: - file_set_error(); - return NULL; } -static struct pipe_user *find_partner(struct named_pipe *pipe) +static struct pipe_user *find_partner(struct named_pipe *pipe, enum pipe_state state) { struct pipe_user *x; for(x = pipe->users; x; x=x->next) { - /* only pair threads that are waiting */ - if(!x->event) - continue; - - /* only pair with pipes that haven't been connected */ - if(x->other_fd == -1) - continue; - + if(x->state==state) break; } + if(!x) + return NULL; + return (struct pipe_user *)grab_object( x ); } @@ -229,6 +241,7 @@ DECL_HANDLER(create_named_pipe) if(user) { + user->state = ps_idle_server; req->handle = alloc_handle( current->process, user, GENERIC_READ|GENERIC_WRITE, 0 ); release_object( user ); } @@ -239,7 +252,6 @@ DECL_HANDLER(create_named_pipe) DECL_HANDLER(open_named_pipe) { struct named_pipe *pipe; - struct pipe_user *user,*partner; req->handle = 0; pipe = create_named_pipe( get_req_data(req), get_req_data_size(req) ); @@ -248,26 +260,43 @@ DECL_HANDLER(open_named_pipe) if (get_error() == STATUS_OBJECT_NAME_COLLISION) { - if ((partner = find_partner(pipe))) + struct pipe_user *partner; + + if ((partner = find_partner(pipe, ps_wait_open))) { - user = create_pipe_user (pipe, partner->other_fd); - if(user) + int fds[2]; + + if(!socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) { - set_event(partner->event); - release_object(partner->event); - partner->event = NULL; - close( partner->other_fd ); - partner->other_fd = -1; - req->handle = alloc_handle( current->process, user, req->access, 0 ); - release_object(user); + struct pipe_user *user; + + if( (user = create_pipe_user (pipe, fds[1])) ) + { + partner->obj.fd = fds[0]; + set_event(partner->event); + release_object(partner->event); + partner->event = NULL; + partner->state = ps_connected_server; + partner->other = user; + user->state = ps_connected_client; + user->other = partner; + req->handle = alloc_handle( current->process, user, req->access, 0 ); + release_object(user); + } + else + { + close(fds[0]); + } } release_object( partner ); } - else { + else + { set_error(STATUS_PIPE_NOT_AVAILABLE); } } - else { + else + { set_error(STATUS_NO_SUCH_FILE); } @@ -276,24 +305,101 @@ DECL_HANDLER(open_named_pipe) DECL_HANDLER(connect_named_pipe) { - struct pipe_user *user; + struct pipe_user *user, *partner; struct event *event; user = get_pipe_user_obj(current->process, req->handle, 0); if(!user) return; - if( user->event || user->other_fd == -1) + if( user->state != ps_idle_server ) { - /* fprintf(stderr,"fd = %x event = %p\n",user->obj.fd,user->event);*/ set_error(STATUS_PORT_ALREADY_SET); } else { + user->state = ps_wait_open; event = get_event_obj(current->process, req->event, 0); if(event) user->event = event; + + /* notify all waiters that a pipe just became available */ + while( (partner = find_partner(user->pipe,ps_wait_connect)) ) + { + set_event(partner->event); + release_object(partner->event); + partner->event = NULL; + release_object(partner); + release_object(partner); + } } release_object(user); } + +DECL_HANDLER(wait_named_pipe) +{ + struct event *event; + struct named_pipe *pipe; + + event = get_event_obj(current->process, req->event, 0); + if(!event) + return; + + pipe = create_named_pipe( get_req_data(req), get_req_data_size(req) ); + if( pipe ) + { + /* only wait if the pipe already exists */ + if(get_error() == STATUS_OBJECT_NAME_COLLISION) + { + struct pipe_user *partner; + + set_error(STATUS_SUCCESS); + if( (partner = find_partner(pipe,ps_wait_open)) ) + { + set_event(event); + release_object(partner); + } + else + { + struct pipe_user *user; + + if( (user = create_pipe_user (pipe, -1)) ) + { + user->event = (struct event *)grab_object( event ); + user->state = ps_wait_connect; + /* don't release it */ + } + } + } + else + { + set_error(STATUS_PIPE_NOT_AVAILABLE); + } + release_object(pipe); + } + release_object(event); +} + +DECL_HANDLER(disconnect_named_pipe) +{ + struct pipe_user *user; + + user = get_pipe_user_obj(current->process, req->handle, 0); + if(!user) + return; + if( (user->state == ps_connected_server) && + (user->other->state == ps_connected_client) ) + { + close(user->other->obj.fd); + user->other->obj.fd = -1; + user->other->state = ps_disconnected; + user->other->other = NULL; + + close(user->obj.fd); + user->obj.fd = -1; + user->state = ps_idle_server; + user->other = NULL; + } + release_object(user); +} diff --git a/server/protocol.def b/server/protocol.def index a4a7af8b788..1fdb2ef8279 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1368,3 +1368,17 @@ enum message_type handle_t handle; handle_t event; /* set this event when it's ready */ @END + + +/* Wait for a named pipe */ +@REQ(wait_named_pipe) + unsigned int timeout; + handle_t event; /* set this event when it's ready */ + VARARG(filename,string); /* pipe name */ +@END + + +/* Disconnect a named pipe */ +@REQ(disconnect_named_pipe) + handle_t handle; +@END diff --git a/server/request.h b/server/request.h index c9882b57feb..f0852ce5536 100644 --- a/server/request.h +++ b/server/request.h @@ -185,6 +185,8 @@ DECL_HANDLER(create_async); DECL_HANDLER(create_named_pipe); DECL_HANDLER(open_named_pipe); DECL_HANDLER(connect_named_pipe); +DECL_HANDLER(wait_named_pipe); +DECL_HANDLER(disconnect_named_pipe); #ifdef WANT_REQUEST_HANDLERS @@ -311,6 +313,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_create_named_pipe, (req_handler)req_open_named_pipe, (req_handler)req_connect_named_pipe, + (req_handler)req_wait_named_pipe, + (req_handler)req_disconnect_named_pipe, }; #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/trace.c b/server/trace.c index 225be9c0242..71350df6814 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1626,6 +1626,19 @@ static void dump_connect_named_pipe_request( const struct connect_named_pipe_req fprintf( stderr, " event=%d", req->event ); } +static void dump_wait_named_pipe_request( const struct wait_named_pipe_request *req ) +{ + fprintf( stderr, " timeout=%08x,", req->timeout ); + fprintf( stderr, " event=%d,", req->event ); + fprintf( stderr, " filename=" ); + cur_pos += dump_varargs_string( req ); +} + +static void dump_disconnect_named_pipe_request( const struct disconnect_named_pipe_request *req ) +{ + fprintf( stderr, " handle=%d", req->handle ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -1747,6 +1760,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_named_pipe_request, (dump_func)dump_open_named_pipe_request, (dump_func)dump_connect_named_pipe_request, + (dump_func)dump_wait_named_pipe_request, + (dump_func)dump_disconnect_named_pipe_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -1870,6 +1885,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_named_pipe_reply, (dump_func)dump_open_named_pipe_reply, (dump_func)0, + (dump_func)0, + (dump_func)0, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -1993,6 +2010,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "create_named_pipe", "open_named_pipe", "connect_named_pipe", + "wait_named_pipe", + "disconnect_named_pipe", }; /* ### make_requests end ### */