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 | |
45 | static void close_cb(uv_handle_t* handle) { |
46 | close_cb_called++; |
47 | } |
48 | |
49 | |
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(exit_status == 0); |
56 | ASSERT(term_signal == 0); |
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 | |
63 | static void init_process_options(char* test, uv_exit_cb exit_cb) { |
64 | int r = uv_exepath(exepath, &exepath_size); |
65 | ASSERT(r == 0); |
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 | |
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 | |
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 | |
97 | static void on_read(uv_stream_t* tcp, 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) { |
107 | ASSERT(memcmp("hello world\n" , output, 12) == 0); |
108 | wrbuf = uv_buf_init(output, output_used); |
109 | req = malloc(sizeof(*req)); |
110 | r = uv_write(req, (uv_stream_t*)&in, &wrbuf, 1, after_write); |
111 | ASSERT(r == 0); |
112 | } |
113 | } |
114 | |
115 | on_read_cb_called++; |
116 | } |
117 | |
118 | |
119 | TEST_IMPL(stdio_over_pipes) { |
120 | int r; |
121 | uv_process_t process; |
122 | uv_stdio_container_t stdio[2]; |
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 | options.stdio[0].data.stream = (uv_stream_t*)∈ |
134 | options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; |
135 | options.stdio[1].data.stream = (uv_stream_t*)&out; |
136 | options.stdio_count = 2; |
137 | |
138 | r = uv_spawn(loop, &process, &options); |
139 | ASSERT(r == 0); |
140 | |
141 | r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); |
142 | ASSERT(r == 0); |
143 | |
144 | r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); |
145 | ASSERT(r == 0); |
146 | |
147 | ASSERT(on_read_cb_called > 1); |
148 | ASSERT(after_write_cb_called == 1); |
149 | ASSERT(exit_cb_called == 1); |
150 | ASSERT(close_cb_called == 3); |
151 | ASSERT(memcmp("hello world\n" , output, 12) == 0); |
152 | ASSERT(output_used == 12); |
153 | |
154 | MAKE_VALGRIND_HAPPY(); |
155 | return 0; |
156 | } |
157 | |
158 | |
159 | /* Everything here runs in a child process. */ |
160 | |
161 | static int on_pipe_read_called; |
162 | static int after_write_called; |
163 | static uv_pipe_t stdin_pipe; |
164 | static uv_pipe_t stdout_pipe; |
165 | |
166 | static void on_pipe_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { |
167 | ASSERT(nread > 0); |
168 | ASSERT(memcmp("hello world\n" , buf->base, nread) == 0); |
169 | on_pipe_read_called++; |
170 | |
171 | free(buf->base); |
172 | |
173 | uv_close((uv_handle_t*)&stdin_pipe, close_cb); |
174 | uv_close((uv_handle_t*)&stdout_pipe, close_cb); |
175 | } |
176 | |
177 | |
178 | static void after_pipe_write(uv_write_t* req, int status) { |
179 | ASSERT(status == 0); |
180 | after_write_called++; |
181 | } |
182 | |
183 | |
184 | static void on_read_alloc(uv_handle_t* handle, |
185 | size_t suggested_size, |
186 | uv_buf_t* buf) { |
187 | buf->base = malloc(suggested_size); |
188 | buf->len = suggested_size; |
189 | } |
190 | |
191 | |
192 | int stdio_over_pipes_helper(void) { |
193 | /* Write several buffers to test that the write order is preserved. */ |
194 | char* buffers[] = { |
195 | "he" , |
196 | "ll" , |
197 | "o " , |
198 | "wo" , |
199 | "rl" , |
200 | "d" , |
201 | "\n" |
202 | }; |
203 | |
204 | uv_write_t write_req[ARRAY_SIZE(buffers)]; |
205 | uv_buf_t buf[ARRAY_SIZE(buffers)]; |
206 | unsigned int i; |
207 | int r; |
208 | uv_loop_t* loop = uv_default_loop(); |
209 | |
210 | ASSERT(UV_NAMED_PIPE == uv_guess_handle(0)); |
211 | ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); |
212 | |
213 | r = uv_pipe_init(loop, &stdin_pipe, 0); |
214 | ASSERT(r == 0); |
215 | r = uv_pipe_init(loop, &stdout_pipe, 0); |
216 | ASSERT(r == 0); |
217 | |
218 | uv_pipe_open(&stdin_pipe, 0); |
219 | uv_pipe_open(&stdout_pipe, 1); |
220 | |
221 | /* Unref both stdio handles to make sure that all writes complete. */ |
222 | uv_unref((uv_handle_t*)&stdin_pipe); |
223 | uv_unref((uv_handle_t*)&stdout_pipe); |
224 | |
225 | for (i = 0; i < ARRAY_SIZE(buffers); i++) { |
226 | buf[i] = uv_buf_init((char*)buffers[i], strlen(buffers[i])); |
227 | } |
228 | |
229 | for (i = 0; i < ARRAY_SIZE(buffers); i++) { |
230 | r = uv_write(&write_req[i], (uv_stream_t*)&stdout_pipe, &buf[i], 1, |
231 | after_pipe_write); |
232 | ASSERT(r == 0); |
233 | } |
234 | |
235 | notify_parent_process(); |
236 | uv_run(loop, UV_RUN_DEFAULT); |
237 | |
238 | ASSERT(after_write_called == 7); |
239 | ASSERT(on_pipe_read_called == 0); |
240 | ASSERT(close_cb_called == 0); |
241 | |
242 | uv_ref((uv_handle_t*)&stdout_pipe); |
243 | uv_ref((uv_handle_t*)&stdin_pipe); |
244 | |
245 | r = uv_read_start((uv_stream_t*)&stdin_pipe, on_read_alloc, on_pipe_read); |
246 | ASSERT(r == 0); |
247 | |
248 | uv_run(loop, UV_RUN_DEFAULT); |
249 | |
250 | ASSERT(after_write_called == 7); |
251 | ASSERT(on_pipe_read_called == 1); |
252 | ASSERT(close_cb_called == 2); |
253 | |
254 | MAKE_VALGRIND_HAPPY(); |
255 | return 0; |
256 | } |
257 | |