/* * Server-side pipe management * * Copyright (C) 1998 Alexandre Julliard */ #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" struct named_pipe; struct pipe_user { struct object obj; int other_fd; 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 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 */ no_get_file_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 (%s)\n", user, (user->other_fd != -1) ? "server" : "client" ); } 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; } /* 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 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; 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] ); if(!user) { if (fds[1] != -1) close( fds[1] ); return NULL; } user->pipe = pipe; user->other_fd = fds[1]; user->event = NULL; /* thread wait on this pipe */ /* 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; error: file_set_error(); return NULL; } static struct pipe_user *find_partner(struct named_pipe *pipe) { 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; break; } 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; user = create_pipe_user (pipe, -1); if(user) { 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; struct pipe_user *user,*partner; 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) { if ((partner = find_partner(pipe))) { user = create_pipe_user (pipe, partner->other_fd); if(user) { 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); } 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; struct event *event; user = get_pipe_user_obj(current->process, req->handle, 0); if(!user) return; if( user->event || user->other_fd == -1) { /* fprintf(stderr,"fd = %x event = %p\n",user->obj.fd,user->event);*/ set_error(STATUS_PORT_ALREADY_SET); } else { event = get_event_obj(current->process, req->event, 0); if(event) user->event = event; } release_object(user); }