1#define VIRTUAL_PIPE_FLAG_WRITE 1 2#define VIRTUAL_PIPE_FLAG_NONBLOCK 2 3 4struct virtual_pipe { 5 unsigned char content[VIRTUAL_PIPE_SIZE]; 6 unsigned used; 7 unsigned n_rd; 8 unsigned n_wr; 9}; 10 11static struct virtual_pipe *pipe_desc[FD_SETSIZE] = { NULL }; 12static unsigned pipe_flags[FD_SETSIZE] = { 0 }; 13 14static struct virtual_pipe *get_virtual_pipe(int fd) 15{ 16 struct virtual_pipe *desc; 17 if (fd >= FD_SETSIZE || !pipe_desc[fd]) return NULL; 18 pipe_lock(); 19 desc = pipe_desc[fd]; 20 if (!desc) { 21 pipe_unlock(); 22 return NULL; 23 } 24 return desc; 25} 26 27static int vpipe_read(int fd, void *buf, size_t size) 28{ 29 int should_wake; 30 struct virtual_pipe *desc; 31test_again: 32 if (!(desc = get_virtual_pipe(fd))) 33 return -2; 34 if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE) internal_error("vpipe_read: reading from write pipe descriptor"); 35 if (!desc->used) { 36 if (!desc->n_wr) { 37#ifdef TRACE_PIPES 38 fprintf(stderr, "read(%d) -> zero.", fd); 39#endif 40 pipe_unlock(); 41 return 0; 42 } 43 if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_NONBLOCK) { 44#ifdef TRACE_PIPES 45 fprintf(stderr, "read(%d) -> wouldblock.", fd); 46#endif 47 pipe_unlock(); 48 errno = EWOULDBLOCK; 49 return -1; 50 } 51#ifdef TRACE_PIPES 52 fprintf(stderr, "read(%d) -> sleep.", fd); 53#endif 54 pipe_unlock_wait(); 55 pipe_unlock(); 56 goto test_again; 57 } 58 should_wake = desc->used == VIRTUAL_PIPE_SIZE; 59 if (size > desc->used) 60 size = desc->used; 61 memcpy(buf, desc->content, size); 62 memmove(desc->content, desc->content + size, desc->used -= size); 63#ifdef TRACE_PIPES 64 fprintf(stderr, "read(%d) -> %d. (%d)", fd, size, should_wake); 65#endif 66 pipe_unlock(); 67 if (should_wake) pipe_wake(); 68 return size; 69} 70 71static int vpipe_write(int fd, const void *buf, size_t size) 72{ 73 int should_wake; 74 struct virtual_pipe *desc; 75test_again: 76 if (!(desc = get_virtual_pipe(fd))) 77 return -2; 78 if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) internal_error("vpipe_write: writing to read pipe descriptor"); 79 if (!desc->n_rd) { 80#ifdef TRACE_PIPES 81 fprintf(stderr, "write(%d) -> epipe.", fd); 82#endif 83 pipe_unlock(); 84 errno = EPIPE; 85 return -1; 86 } 87 if (desc->used == VIRTUAL_PIPE_SIZE) { 88 if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_NONBLOCK) { 89#ifdef TRACE_PIPES 90 fprintf(stderr, "write(%d) -> wouldblock.", fd); 91#endif 92 pipe_unlock(); 93 errno = EWOULDBLOCK; 94 return -1; 95 } 96#ifdef TRACE_PIPES 97 fprintf(stderr, "write(%d) -> sleep.", fd); 98#endif 99 pipe_unlock_wait(); 100 pipe_unlock(); 101 goto test_again; 102 } 103 should_wake = !desc->used; 104 if (size > (VIRTUAL_PIPE_SIZE - desc->used)) 105 size = VIRTUAL_PIPE_SIZE - desc->used; 106 memcpy(desc->content + desc->used, buf, size); 107 desc->used += size; 108#ifdef TRACE_PIPES 109 fprintf(stderr, "write(%d) -> %d.", fd, size); 110#endif 111 pipe_unlock(); 112 if (should_wake) pipe_wake(); 113 return size; 114} 115 116static int vpipe_close(int fd) 117{ 118 struct virtual_pipe *desc; 119 if (!(desc = get_virtual_pipe(fd))) 120 return -2; 121 if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) { 122 if (!desc->n_rd) internal_error("vpipe_close: read counter underflow"); 123 desc->n_rd--; 124 } else { 125 if (!desc->n_wr) internal_error("vpipe_close: write counter underflow"); 126 desc->n_wr--; 127 } 128 pipe_desc[fd] = NULL; 129 pipe_flags[fd] = 0; 130 pipe_unlock(); 131 if (!desc->n_rd && !desc->n_wr) 132 free(desc); 133 return close(fd); 134} 135 136static int vpipe_create(int fd[2]) 137{ 138 int rs; 139 struct virtual_pipe *desc; 140 EINTRLOOP(fd[0], open("/dev/null", O_RDONLY)); 141 if (fd[0] == -1) 142 goto err0; 143 EINTRLOOP(fd[1], open("/dev/null", O_WRONLY)); 144 if (fd[1] == -1) 145 goto err1; 146 if (fd[0] >= FD_SETSIZE || fd[1] >= FD_SETSIZE) { 147 errno = EMFILE; 148 goto err2; 149 } 150 desc = malloc(sizeof(struct virtual_pipe)); 151 if (!desc) goto err2; 152 desc->used = 0; 153 desc->n_rd = 1; 154 desc->n_wr = 1; 155 pipe_lock(); 156 if (pipe_desc[fd[0]] || pipe_flags[fd[0]] || pipe_desc[fd[1]] || pipe_flags[fd[0]]) 157 internal_error("c_pipe: pipe handles %d, %d already used: %p, %d, %p, %d", fd[0], fd[1], pipe_desc[fd[0]], pipe_flags[fd[0]], pipe_desc[fd[1]], pipe_flags[fd[0]]); 158 pipe_desc[fd[0]] = desc; 159 pipe_flags[fd[0]] = 0; 160 pipe_desc[fd[1]] = desc; 161 pipe_flags[fd[1]] = VIRTUAL_PIPE_FLAG_WRITE; 162#ifdef DOS 163 pipe_flags[fd[0]] |= VIRTUAL_PIPE_FLAG_NONBLOCK; 164 pipe_flags[fd[1]] |= VIRTUAL_PIPE_FLAG_NONBLOCK; 165#endif 166 pipe_unlock(); 167 return 0; 168err2: 169 EINTRLOOP(rs, close(fd[1])); 170err1: 171 EINTRLOOP(rs, close(fd[0])); 172err0: 173 return -1; 174} 175 176static int vpipe_may_read(int fd) 177{ 178 if (pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE) internal_error("vpipe_may_read: selecting write descriptor %d for read", fd); 179 return pipe_desc[fd]->used || !pipe_desc[fd]->n_wr; 180} 181 182static int vpipe_may_write(int fd) 183{ 184 if (!(pipe_flags[fd] & VIRTUAL_PIPE_FLAG_WRITE)) internal_error("vpipe_may_write: selecting read descriptor %d for write", fd); 185 return pipe_desc[fd]->used != VIRTUAL_PIPE_SIZE || !pipe_desc[fd]->n_rd; 186} 187 188static void clear_inactive(fd_set *fs, int i) 189{ 190 if (!fs) return; 191 while (--i >= 0) FD_CLR(i, fs); 192} 193 194