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 29static char exepath[1024]; 30static size_t exepath_size = 1024; 31static char* args[3]; 32static uv_process_options_t options; 33static int close_cb_called; 34static int exit_cb_called; 35static int on_read_cb_called; 36static int after_write_cb_called; 37static uv_pipe_t in; 38static uv_pipe_t out; 39static uv_loop_t* loop; 40#define OUTPUT_SIZE 1024 41static char output[OUTPUT_SIZE]; 42static int output_used; 43 44 45static void close_cb(uv_handle_t* handle) { 46 close_cb_called++; 47} 48 49 50static 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 63static 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 76static 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 84static 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 97static 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(memcmp("hello world\n", output, 12) == 0); 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(r == 0); 112 } 113 } 114 115 on_read_cb_called++; 116} 117 118 119static 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(r == 0); 144 145 r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); 146 ASSERT(r == 0); 147 148 r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); 149 ASSERT(r == 0); 150 151 ASSERT(on_read_cb_called > 1); 152 ASSERT(after_write_cb_called == 2); 153 ASSERT(exit_cb_called == 1); 154 ASSERT(close_cb_called == 3); 155 ASSERT(memcmp("hello world\nhello world\n", output, 24) == 0); 156 ASSERT(output_used == 24); 157 158 MAKE_VALGRIND_HAPPY(); 159} 160 161TEST_IMPL(stdio_over_pipes) { 162 test_stdio_over_pipes(0); 163 return 0; 164} 165 166TEST_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 174static int on_pipe_read_called; 175static int after_write_called; 176static uv_pipe_t stdin_pipe1; 177static uv_pipe_t stdout_pipe1; 178static uv_pipe_t stdin_pipe2; 179static uv_pipe_t stdout_pipe2; 180 181static void on_pipe_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) { 182 ASSERT(nread > 0); 183 ASSERT(memcmp("hello world\n", buf->base, nread) == 0); 184 on_pipe_read_called++; 185 186 free(buf->base); 187 188 uv_read_stop(pipe); 189} 190 191 192static void after_pipe_write(uv_write_t* req, int status) { 193 ASSERT(status == 0); 194 after_write_called++; 195} 196 197 198static 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 206int 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(UV_NAMED_PIPE == uv_guess_handle(0)); 226 ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); 227 228 r = uv_pipe_init(loop, &stdin_pipe1, 0); 229 ASSERT(r == 0); 230 r = uv_pipe_init(loop, &stdout_pipe1, 0); 231 ASSERT(r == 0); 232 r = uv_pipe_init(loop, &stdin_pipe2, 0); 233 ASSERT(r == 0); 234 r = uv_pipe_init(loop, &stdout_pipe2, 0); 235 ASSERT(r == 0); 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(r == 0); 260 } 261 262 notify_parent_process(); 263 uv_run(loop, UV_RUN_DEFAULT); 264 265 ASSERT(after_write_called == 7 * (j + 1)); 266 ASSERT(on_pipe_read_called == j); 267 ASSERT(close_cb_called == 0); 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(r == 0); 278 279 uv_run(loop, UV_RUN_DEFAULT); 280 281 ASSERT(after_write_called == 7 * (j + 1)); 282 ASSERT(on_pipe_read_called == j + 1); 283 ASSERT(close_cb_called == 0); 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(after_write_called == 14); 294 ASSERT(on_pipe_read_called == 2); 295 ASSERT(close_cb_called == 4); 296 297 MAKE_VALGRIND_HAPPY(); 298 return 0; 299} 300