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