/* * Server-side pipe management * * Copyright (C) 1998 Alexandre Julliard */ #include #include #include #include #include #include #include #include #include #include #include "winerror.h" #include "winnt.h" #include "server/thread.h" enum side { READ_SIDE, WRITE_SIDE }; struct pipe { struct object obj; /* object header */ struct pipe *other; /* the pipe other end */ int fd; /* Unix file descriptor */ enum side side; /* which side of the pipe is this */ }; static void pipe_dump( struct object *obj, int verbose ); static void pipe_add_queue( struct object *obj, struct wait_queue_entry *entry ); static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry ); static int pipe_signaled( struct object *obj, struct thread *thread ); static int pipe_get_read_fd( struct object *obj ); static int pipe_get_write_fd( struct object *obj ); static void pipe_destroy( struct object *obj ); static const struct object_ops pipe_ops = { pipe_dump, pipe_add_queue, pipe_remove_queue, pipe_signaled, no_satisfied, pipe_get_read_fd, pipe_get_write_fd, no_flush, pipe_destroy }; static const struct select_ops select_ops = { default_select_event, NULL /* we never set a timeout on a pipe */ }; int create_pipe( struct object *obj[2] ) { struct pipe *newpipe[2]; int fd[2]; if (pipe( fd ) == -1) { file_set_error(); return 0; } if (!(newpipe[0] = mem_alloc( sizeof(struct pipe) ))) { close( fd[0] ); close( fd[1] ); return 0; } if (!(newpipe[1] = mem_alloc( sizeof(struct pipe) ))) { close( fd[0] ); close( fd[1] ); free( newpipe[0] ); return 0; } init_object( &newpipe[0]->obj, &pipe_ops, NULL ); init_object( &newpipe[1]->obj, &pipe_ops, NULL ); newpipe[0]->fd = fd[0]; newpipe[0]->other = newpipe[1]; newpipe[0]->side = READ_SIDE; newpipe[1]->fd = fd[1]; newpipe[1]->other = newpipe[0]; newpipe[1]->side = WRITE_SIDE; obj[0] = &newpipe[0]->obj; obj[1] = &newpipe[1]->obj; CLEAR_ERROR(); return 1; } static void pipe_dump( struct object *obj, int verbose ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); printf( "Pipe %s-side fd=%d\n", (pipe->side == READ_SIDE) ? "read" : "write", pipe->fd ); } static void pipe_add_queue( struct object *obj, struct wait_queue_entry *entry ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); if (!obj->head) /* first on the queue */ add_select_user( pipe->fd, (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT, &select_ops, pipe ); add_queue( obj, entry ); } static void pipe_remove_queue( struct object *obj, struct wait_queue_entry *entry ) { struct pipe *pipe = (struct pipe *)grab_object(obj); assert( obj->ops == &pipe_ops ); remove_queue( obj, entry ); if (!obj->head) /* last on the queue is gone */ remove_select_user( pipe->fd ); release_object( obj ); } static int pipe_signaled( struct object *obj, struct thread *thread ) { struct pipe *pipe = (struct pipe *)obj; struct timeval tv = { 0, 0 }; fd_set fds; assert( obj->ops == &pipe_ops ); FD_ZERO( &fds ); FD_SET( pipe->fd, &fds ); if (pipe->side == READ_SIDE) return select( pipe->fd + 1, &fds, NULL, NULL, &tv ) > 0; else return select( pipe->fd + 1, NULL, &fds, NULL, &tv ) > 0; } static int pipe_get_read_fd( struct object *obj ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); if (!pipe->other) { SET_ERROR( ERROR_BROKEN_PIPE ); return -1; } if (pipe->side != READ_SIDE) /* FIXME: should not be necessary */ { SET_ERROR( ERROR_ACCESS_DENIED ); return -1; } return dup( pipe->fd ); } static int pipe_get_write_fd( struct object *obj ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); if (!pipe->other) { SET_ERROR( ERROR_BROKEN_PIPE ); return -1; } if (pipe->side != WRITE_SIDE) /* FIXME: should not be necessary */ { SET_ERROR( ERROR_ACCESS_DENIED ); return -1; } return dup( pipe->fd ); } static void pipe_destroy( struct object *obj ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); if (pipe->other) pipe->other->other = NULL; close( pipe->fd ); free( pipe ); }