/* * Server-side pipe management * * Copyright (C) 1998 Alexandre Julliard */ #include #include #include #include #include #ifdef HAVE_SYS_ERRNO_H #include #endif #include #include #include #include #include #include "winerror.h" #include "winbase.h" #include "handle.h" #include "thread.h" #include "request.h" enum side { READ_SIDE, WRITE_SIDE }; struct pipe { struct object obj; /* object header */ struct pipe *other; /* the pipe other end */ struct select_user select; /* select user */ enum side side; /* which side of the pipe is this */ }; static void pipe_dump( struct object *obj, int verbose ); static int 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 int pipe_get_info( struct object *obj, struct get_file_info_request *req ); static void pipe_destroy( struct object *obj ); static const struct object_ops pipe_ops = { sizeof(struct pipe), pipe_dump, pipe_add_queue, pipe_remove_queue, pipe_signaled, no_satisfied, pipe_get_read_fd, pipe_get_write_fd, no_flush, pipe_get_info, pipe_destroy }; static struct pipe *create_pipe_side( int fd, int side ) { struct pipe *pipe; if ((pipe = alloc_object( &pipe_ops ))) { pipe->select.fd = fd; pipe->select.func = default_select_event; pipe->select.private = pipe; pipe->other = NULL; pipe->side = side; register_select_user( &pipe->select ); } return pipe; } static int create_pipe( struct object *obj[2] ) { struct pipe *read_pipe; struct pipe *write_pipe; int fd[2]; if (pipe( fd ) == -1) { file_set_error(); return 0; } if ((read_pipe = create_pipe_side( fd[0], READ_SIDE ))) { if ((write_pipe = create_pipe_side( fd[1], WRITE_SIDE ))) { write_pipe->other = read_pipe; read_pipe->other = write_pipe; obj[0] = &read_pipe->obj; obj[1] = &write_pipe->obj; return 1; } release_object( read_pipe ); } close( fd[0] ); close( fd[1] ); return 0; } static void pipe_dump( struct object *obj, int verbose ) { struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); fprintf( stderr, "Pipe %s-side fd=%d\n", (pipe->side == READ_SIDE) ? "read" : "write", pipe->select.fd ); } static int 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 */ set_select_events( &pipe->select, (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT ); add_queue( obj, entry ); return 1; } 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 */ set_select_events( &pipe->select, 0 ); release_object( obj ); } static int pipe_signaled( struct object *obj, struct thread *thread ) { int event; struct pipe *pipe = (struct pipe *)obj; assert( obj->ops == &pipe_ops ); event = (pipe->side == READ_SIDE) ? READ_EVENT : WRITE_EVENT; if (check_select_events( &pipe->select, event )) { /* stop waiting on select() if we are signaled */ set_select_events( &pipe->select, 0 ); return 1; } else { /* restart waiting on select() if we are no longer signaled */ if (obj->head) set_select_events( &pipe->select, event ); return 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->select.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->select.fd ); } static int pipe_get_info( struct object *obj, struct get_file_info_request *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 1; } 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; unregister_select_user( &pipe->select ); close( pipe->select.fd ); } /* create an anonymous pipe */ DECL_HANDLER(create_pipe) { struct object *obj[2]; int hread = -1, hwrite = -1; if (create_pipe( obj )) { hread = alloc_handle( current->process, obj[0], STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_READ, req->inherit ); if (hread != -1) { hwrite = alloc_handle( current->process, obj[1], STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|GENERIC_WRITE, req->inherit ); if (hwrite == -1) close_handle( current->process, hread ); } release_object( obj[0] ); release_object( obj[1] ); } req->handle_read = hread; req->handle_write = hwrite; }