| /* |
| * Server-side pipe management |
| * |
| * Copyright (C) 1998 Alexandre Julliard |
| */ |
| |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/errno.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #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 ); |
| } |