/* * Server-side pipe management * * Copyright (C) 1998 Alexandre Julliard * Copyright (C) 2001 Mike McCormack * * TODO: * improve error handling */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "winbase.h" #include "handle.h" #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; enum pipe_state state; struct pipe_user *other; struct named_pipe *pipe; struct pipe_user *next; struct pipe_user *prev; struct event *event; }; struct named_pipe { struct object obj; /* object header */ unsigned int pipemode; unsigned int maxinstances; unsigned int outsize; unsigned int insize; unsigned int timeout; struct pipe_user *users; }; static void named_pipe_dump( struct object *obj, int verbose ); static void named_pipe_destroy( struct object *obj); static const struct object_ops named_pipe_ops = { sizeof(struct named_pipe), /* size */ named_pipe_dump, /* dump */ no_add_queue, /* add_queue */ NULL, /* remove_queue */ NULL, /* signaled */ NULL, /* satisfied */ NULL, /* get_poll_events */ NULL, /* poll_event */ no_get_fd, /* get_fd */ no_flush, /* flush */ no_get_file_info, /* get_file_info */ named_pipe_destroy /* destroy */ }; static void pipe_user_dump( struct object *obj, int verbose ); static void pipe_user_destroy( struct object *obj); static int pipe_user_get_fd( struct object *obj ); static int pipe_user_get_info( struct object *obj, struct get_file_info_request *req ); static const struct object_ops pipe_user_ops = { sizeof(struct pipe_user), /* size */ pipe_user_dump, /* dump */ default_poll_add_queue, /* add_queue */ default_poll_remove_queue, /* remove_queue */ default_poll_signaled, /* signaled */ no_satisfied, /* satisfied */ NULL, /* get_poll_events */ default_poll_event, /* poll_event */ pipe_user_get_fd, /* get_fd */ no_flush, /* flush */ pipe_user_get_info, /* get_file_info */ pipe_user_destroy /* destroy */ }; static void named_pipe_dump( struct object *obj, int verbose ) { struct named_pipe *pipe = (struct named_pipe *)obj; assert( obj->ops == &named_pipe_ops ); fprintf( stderr, "named pipe %p\n" ,pipe); } 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 (state %d)\n", user, user->state ); } static void named_pipe_destroy( struct object *obj) { struct named_pipe *pipe = (struct named_pipe *)obj; assert( !pipe->users ); } static void pipe_user_destroy( struct object *obj) { struct pipe_user *user = (struct pipe_user *)obj; assert( obj->ops == &pipe_user_ops ); if(user->event) { /* FIXME: signal waiter of failure */ 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; if (user->prev) user->prev->next = user->next; else user->pipe->users = user->next; release_object(user->pipe); } static int pipe_user_get_fd( struct object *obj ) { struct pipe_user *user = (struct pipe_user *)obj; assert( obj->ops == &pipe_user_ops ); return user->obj.fd; } static int pipe_user_get_info( struct object *obj, struct get_file_info_request *req ) { if (req) { req->type = FILE_TYPE_PIPE; req->attr = 0; req->access_time = 0; req->write_time = 0; req->size_high = 0; req->size_low = 0; req->links = 0; req->index_high = 0; req->index_low = 0; req->serial = 0; } return FD_TYPE_DEFAULT; } static struct named_pipe *create_named_pipe( const WCHAR *name, size_t len ) { struct named_pipe *pipe; if ((pipe = create_named_object( &named_pipe_ops, name, len ))) { if (get_error() != STATUS_OBJECT_NAME_COLLISION) { /* initialize it if it didn't already exist */ pipe->users = 0; } } return pipe; } static struct pipe_user *get_pipe_user_obj( struct process *process, handle_t handle, unsigned int access ) { return (struct pipe_user *)get_handle_obj( process, handle, access, &pipe_user_ops ); } static struct pipe_user *create_pipe_user( struct named_pipe *pipe, int fd ) { struct pipe_user *user; user = alloc_object( &pipe_user_ops, fd ); if(!user) return NULL; user->pipe = pipe; 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; user->prev = NULL; pipe->users = user; grab_object(pipe); return user; } 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) { if(x->state==state) break; } if(!x) return NULL; return (struct pipe_user *)grab_object( x ); } DECL_HANDLER(create_named_pipe) { struct named_pipe *pipe; struct pipe_user *user; req->handle = 0; pipe = create_named_pipe( get_req_data(req), get_req_data_size(req) ); if(!pipe) return; if (get_error() != STATUS_OBJECT_NAME_COLLISION) { pipe->insize = req->insize; pipe->outsize = req->outsize; pipe->maxinstances = req->maxinstances; pipe->timeout = req->timeout; pipe->pipemode = req->pipemode; } user = create_pipe_user (pipe, -1); if(user) { user->state = ps_idle_server; req->handle = alloc_handle( current->process, user, GENERIC_READ|GENERIC_WRITE, 0 ); release_object( user ); } release_object( pipe ); } DECL_HANDLER(open_named_pipe) { struct named_pipe *pipe; req->handle = 0; pipe = create_named_pipe( get_req_data(req), get_req_data_size(req) ); if(!pipe) return; if (get_error() == STATUS_OBJECT_NAME_COLLISION) { struct pipe_user *partner; if ((partner = find_partner(pipe, ps_wait_open))) { int fds[2]; if(!socketpair(PF_UNIX, SOCK_STREAM, 0, fds)) { 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 { set_error(STATUS_PIPE_NOT_AVAILABLE); } } else { set_error(STATUS_NO_SUCH_FILE); } release_object(pipe); } DECL_HANDLER(connect_named_pipe) { struct pipe_user *user, *partner; struct event *event; user = get_pipe_user_obj(current->process, req->handle, 0); if(!user) return; if( user->state != ps_idle_server ) { 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); } DECL_HANDLER(get_named_pipe_info) { struct pipe_user *user; user = get_pipe_user_obj(current->process, req->handle, 0); if(!user) return; req->flags = user->pipe->pipemode; req->maxinstances = user->pipe->maxinstances; req->insize = user->pipe->insize; req->outsize = user->pipe->outsize; release_object(user); }