1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 */
21
22 #include "uv.h"
23 #include "task.h"
24
25 #include <stdlib.h>
26 #include <string.h>
27
28
29 static char exepath[1024];
30 static size_t exepath_size = 1024;
31 static char* args[3];
32 static uv_process_options_t options;
33 static int close_cb_called;
34 static int exit_cb_called;
35 static int on_read_cb_called;
36 static int after_write_cb_called;
37 static uv_pipe_t in;
38 static uv_pipe_t out;
39 static uv_loop_t* loop;
40 #define OUTPUT_SIZE 1024
41 static char output[OUTPUT_SIZE];
42 static int output_used;
43
44
close_cb(uv_handle_t * handle)45 static void close_cb(uv_handle_t* handle) {
46 close_cb_called++;
47 }
48
49
exit_cb(uv_process_t * process,int64_t exit_status,int term_signal)50 static void exit_cb(uv_process_t* process,
51 int64_t exit_status,
52 int term_signal) {
53 printf("exit_cb\n");
54 exit_cb_called++;
55 ASSERT_OK(exit_status);
56 ASSERT_OK(term_signal);
57 uv_close((uv_handle_t*)process, close_cb);
58 uv_close((uv_handle_t*)&in, close_cb);
59 uv_close((uv_handle_t*)&out, close_cb);
60 }
61
62
init_process_options(char * test,uv_exit_cb exit_cb)63 static void init_process_options(char* test, uv_exit_cb exit_cb) {
64 int r = uv_exepath(exepath, &exepath_size);
65 ASSERT_OK(r);
66 exepath[exepath_size] = '\0';
67 args[0] = exepath;
68 args[1] = test;
69 args[2] = NULL;
70 options.file = exepath;
71 options.args = args;
72 options.exit_cb = exit_cb;
73 }
74
75
on_alloc(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)76 static void on_alloc(uv_handle_t* handle,
77 size_t suggested_size,
78 uv_buf_t* buf) {
79 buf->base = output + output_used;
80 buf->len = OUTPUT_SIZE - output_used;
81 }
82
83
after_write(uv_write_t * req,int status)84 static void after_write(uv_write_t* req, int status) {
85 if (status) {
86 fprintf(stderr, "uv_write error: %s\n", uv_strerror(status));
87 ASSERT(0);
88 }
89
90 /* Free the read/write buffer and the request */
91 free(req);
92
93 after_write_cb_called++;
94 }
95
96
on_read(uv_stream_t * pipe,ssize_t nread,const uv_buf_t * rdbuf)97 static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* rdbuf) {
98 uv_write_t* req;
99 uv_buf_t wrbuf;
100 int r;
101
102 ASSERT(nread > 0 || nread == UV_EOF);
103
104 if (nread > 0) {
105 output_used += nread;
106 if (output_used % 12 == 0) {
107 ASSERT_OK(memcmp("hello world\n", output, 12));
108 wrbuf = uv_buf_init(output, 12);
109 req = malloc(sizeof(*req));
110 r = uv_write(req, (uv_stream_t*) &in, &wrbuf, 1, after_write);
111 ASSERT_OK(r);
112 }
113 }
114
115 on_read_cb_called++;
116 }
117
118
test_stdio_over_pipes(int overlapped)119 static void test_stdio_over_pipes(int overlapped) {
120 int r;
121 uv_process_t process;
122 uv_stdio_container_t stdio[3];
123
124 loop = uv_default_loop();
125
126 init_process_options("stdio_over_pipes_helper", exit_cb);
127
128 uv_pipe_init(loop, &out, 0);
129 uv_pipe_init(loop, &in, 0);
130
131 options.stdio = stdio;
132 options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE |
133 (overlapped ? UV_OVERLAPPED_PIPE : 0);
134 options.stdio[0].data.stream = (uv_stream_t*) ∈
135 options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE |
136 (overlapped ? UV_OVERLAPPED_PIPE : 0);
137 options.stdio[1].data.stream = (uv_stream_t*) &out;
138 options.stdio[2].flags = UV_INHERIT_FD;
139 options.stdio[2].data.fd = 2;
140 options.stdio_count = 3;
141
142 r = uv_spawn(loop, &process, &options);
143 ASSERT_OK(r);
144
145 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read);
146 ASSERT_OK(r);
147
148 r = uv_run(loop, UV_RUN_DEFAULT);
149 ASSERT_OK(r);
150
151 ASSERT_GT(on_read_cb_called, 1);
152 ASSERT_EQ(2, after_write_cb_called);
153 ASSERT_EQ(1, exit_cb_called);
154 ASSERT_EQ(3, close_cb_called);
155 ASSERT_OK(memcmp("hello world\nhello world\n", output, 24));
156 ASSERT_EQ(24, output_used);
157
158 MAKE_VALGRIND_HAPPY(loop);
159 }
160
TEST_IMPL(stdio_over_pipes)161 TEST_IMPL(stdio_over_pipes) {
162 test_stdio_over_pipes(0);
163 return 0;
164 }
165
TEST_IMPL(stdio_emulate_iocp)166 TEST_IMPL(stdio_emulate_iocp) {
167 test_stdio_over_pipes(1);
168 return 0;
169 }
170
171
172 /* Everything here runs in a child process. */
173
174 static int on_pipe_read_called;
175 static int after_write_called;
176 static uv_pipe_t stdin_pipe1;
177 static uv_pipe_t stdout_pipe1;
178 static uv_pipe_t stdin_pipe2;
179 static uv_pipe_t stdout_pipe2;
180
on_pipe_read(uv_stream_t * pipe,ssize_t nread,const uv_buf_t * buf)181 static void on_pipe_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) {
182 ASSERT_GT(nread, 0);
183 ASSERT_OK(memcmp("hello world\n", buf->base, nread));
184 on_pipe_read_called++;
185
186 free(buf->base);
187
188 uv_read_stop(pipe);
189 }
190
191
after_pipe_write(uv_write_t * req,int status)192 static void after_pipe_write(uv_write_t* req, int status) {
193 ASSERT_OK(status);
194 after_write_called++;
195 }
196
197
on_read_alloc(uv_handle_t * handle,size_t suggested_size,uv_buf_t * buf)198 static void on_read_alloc(uv_handle_t* handle,
199 size_t suggested_size,
200 uv_buf_t* buf) {
201 buf->base = malloc(suggested_size);
202 buf->len = suggested_size;
203 }
204
205
stdio_over_pipes_helper(void)206 int stdio_over_pipes_helper(void) {
207 /* Write several buffers to test that the write order is preserved. */
208 char* buffers[] = {
209 "he",
210 "ll",
211 "o ",
212 "wo",
213 "rl",
214 "d",
215 "\n"
216 };
217
218 uv_write_t write_req[ARRAY_SIZE(buffers)];
219 uv_buf_t buf[ARRAY_SIZE(buffers)];
220 unsigned int i;
221 int j;
222 int r;
223 uv_loop_t* loop = uv_default_loop();
224
225 ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(0));
226 ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(1));
227
228 r = uv_pipe_init(loop, &stdin_pipe1, 0);
229 ASSERT_OK(r);
230 r = uv_pipe_init(loop, &stdout_pipe1, 0);
231 ASSERT_OK(r);
232 r = uv_pipe_init(loop, &stdin_pipe2, 0);
233 ASSERT_OK(r);
234 r = uv_pipe_init(loop, &stdout_pipe2, 0);
235 ASSERT_OK(r);
236
237 uv_pipe_open(&stdin_pipe1, 0);
238 uv_pipe_open(&stdout_pipe1, 1);
239 uv_pipe_open(&stdin_pipe2, 0);
240 uv_pipe_open(&stdout_pipe2, 1);
241
242 for (j = 0; j < 2; j++) {
243 /* Unref both stdio handles to make sure that all writes complete. */
244 uv_unref((uv_handle_t*) &stdin_pipe1);
245 uv_unref((uv_handle_t*) &stdout_pipe1);
246 uv_unref((uv_handle_t*) &stdin_pipe2);
247 uv_unref((uv_handle_t*) &stdout_pipe2);
248
249 for (i = 0; i < ARRAY_SIZE(buffers); i++) {
250 buf[i] = uv_buf_init((char*) buffers[i], strlen(buffers[i]));
251 }
252
253 for (i = 0; i < ARRAY_SIZE(buffers); i++) {
254 r = uv_write(&write_req[i],
255 (uv_stream_t*) (j == 0 ? &stdout_pipe1 : &stdout_pipe2),
256 &buf[i],
257 1,
258 after_pipe_write);
259 ASSERT_OK(r);
260 }
261
262 notify_parent_process();
263 uv_run(loop, UV_RUN_DEFAULT);
264
265 ASSERT_EQ(after_write_called, 7 * (j + 1));
266 ASSERT_EQ(on_pipe_read_called, j);
267 ASSERT_OK(close_cb_called);
268
269 uv_ref((uv_handle_t*) &stdout_pipe1);
270 uv_ref((uv_handle_t*) &stdin_pipe1);
271 uv_ref((uv_handle_t*) &stdout_pipe2);
272 uv_ref((uv_handle_t*) &stdin_pipe2);
273
274 r = uv_read_start((uv_stream_t*) (j == 0 ? &stdin_pipe1 : &stdin_pipe2),
275 on_read_alloc,
276 on_pipe_read);
277 ASSERT_OK(r);
278
279 uv_run(loop, UV_RUN_DEFAULT);
280
281 ASSERT_EQ(after_write_called, 7 * (j + 1));
282 ASSERT_EQ(on_pipe_read_called, j + 1);
283 ASSERT_OK(close_cb_called);
284 }
285
286 uv_close((uv_handle_t*)&stdin_pipe1, close_cb);
287 uv_close((uv_handle_t*)&stdout_pipe1, close_cb);
288 uv_close((uv_handle_t*)&stdin_pipe2, close_cb);
289 uv_close((uv_handle_t*)&stdout_pipe2, close_cb);
290
291 uv_run(loop, UV_RUN_DEFAULT);
292
293 ASSERT_EQ(14, after_write_called);
294 ASSERT_EQ(2, on_pipe_read_called);
295 ASSERT_EQ(4, close_cb_called);
296
297 MAKE_VALGRIND_HAPPY(loop);
298 return 0;
299 }
300