From de261713bfdffece3a5675fcf325dbcca7542e87 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 27 Jul 2011 03:54:00 +0200 Subject: [PATCH] uv: upgrade to a1adfe3 --- deps/uv/include/uv-unix.h | 3 + deps/uv/include/uv.h | 59 +++++++- deps/uv/msvs/libuv.vcxproj | 1 + deps/uv/src/uv-unix.c | 218 ++++++++++++++++++++++++++-- deps/uv/src/win/internal.h | 5 + deps/uv/src/win/pipe.c | 66 +++++++-- deps/uv/src/win/stdio.c | 75 ++++++++++ deps/uv/src/win/stream.c | 59 +++++--- deps/uv/src/win/tcp.c | 4 +- deps/uv/test/benchmark-pump.c | 14 +- deps/uv/test/dns-server.c | 6 +- deps/uv/test/echo-server.c | 10 +- deps/uv/test/run-tests.c | 15 +- deps/uv/test/test-delayed-accept.c | 6 +- deps/uv/test/test-getsockname.c | 4 +- deps/uv/test/test-list.h | 5 + deps/uv/test/test-pipe-bind-error.c | 6 +- deps/uv/test/test-spawn.c | 134 +++++++++++++++++ deps/uv/test/test-tcp-bind-error.c | 6 +- deps/uv/test/test-tcp-bind6-error.c | 4 +- 20 files changed, 620 insertions(+), 80 deletions(-) create mode 100644 deps/uv/src/win/stdio.c create mode 100644 deps/uv/test/test-spawn.c diff --git a/deps/uv/include/uv-unix.h b/deps/uv/include/uv-unix.h index cad2ed4abe..72c66a9f71 100644 --- a/deps/uv/include/uv-unix.h +++ b/deps/uv/include/uv-unix.h @@ -132,4 +132,7 @@ typedef struct { struct addrinfo* res; \ int retcode; +#define UV_PROCESS_PRIVATE_FIELDS \ + ev_child child_watcher; + #endif /* UV_UNIX_H */ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index 6f302dcd17..d72caa6df8 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -50,6 +50,7 @@ typedef struct uv_check_s uv_check_t; typedef struct uv_idle_s uv_idle_t; typedef struct uv_async_s uv_async_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; +typedef struct uv_process_s uv_process_t; /* Request types */ typedef struct uv_req_s uv_req_t; typedef struct uv_shutdown_s uv_shutdown_t; @@ -76,7 +77,7 @@ typedef void (*uv_read_cb)(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf); typedef void (*uv_write_cb)(uv_write_t* req, int status); typedef void (*uv_connect_cb)(uv_connect_t* req, int status); typedef void (*uv_shutdown_cb)(uv_shutdown_t* req, int status); -typedef void (*uv_connection_cb)(uv_handle_t* server, int status); +typedef void (*uv_connection_cb)(uv_stream_t* server, int status); typedef void (*uv_close_cb)(uv_handle_t* handle); typedef void (*uv_timer_cb)(uv_timer_t* handle, int status); /* TODO: do these really need a status argument? */ @@ -85,6 +86,7 @@ typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status); typedef void (*uv_check_cb)(uv_check_t* handle, int status); typedef void (*uv_idle_cb)(uv_idle_t* handle, int status); typedef void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* handle, int status, struct addrinfo* res); +typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal); /* Expand this list if necessary. */ @@ -145,7 +147,8 @@ typedef enum { UV_ASYNC, UV_ARES_TASK, UV_ARES_EVENT, - UV_GETADDRINFO + UV_GETADDRINFO, + UV_PROCESS } uv_handle_type; typedef enum { @@ -244,6 +247,8 @@ struct uv_stream_s { UV_STREAM_FIELDS }; +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); + /* This call is used in conjunction with uv_listen() to accept incoming * connections. Call uv_accept after receiving a uv_connection_cb to accept * the connection. Before calling uv_accept use uv_*_init() must be @@ -254,7 +259,7 @@ struct uv_stream_s { * once, it may fail. It is suggested to only call uv_accept once per * uv_connection_cb call. */ -int uv_accept(uv_handle_t* server, uv_stream_t* client); +int uv_accept(uv_stream_t* server, uv_stream_t* client); /* Read data from an incoming stream. The callback will be made several * several times until there is no more data to read or uv_read_stop is @@ -269,6 +274,14 @@ int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); int uv_read_stop(uv_stream_t*); +typedef enum { + UV_STDIN = 0, + UV_STDOUT, + UV_STDERR +} uv_std_type; + +uv_stream_t* uv_std_handle(uv_std_type type); + /* * Write data to stream. Buffers are written in order. Example: * @@ -332,8 +345,6 @@ int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, int uv_tcp_connect6(uv_connect_t* req, uv_tcp_t* handle, struct sockaddr_in6 address, uv_connect_cb cb); -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); - int uv_getsockname(uv_tcp_t* handle, struct sockaddr* name, int* namelen); @@ -350,8 +361,6 @@ int uv_pipe_init(uv_pipe_t* handle); int uv_pipe_bind(uv_pipe_t* handle, const char* name); -int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb); - int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb); @@ -489,6 +498,41 @@ struct uv_getaddrinfo_s { const char* service, const struct addrinfo* hints); +/* + * Child process. Subclass of uv_handle_t. + */ +typedef struct uv_process_options_s { + uv_exit_cb exit_cb; + const char* file; + char** args; + char** env; + char* cwd; + /* + * The user should supply pointers to uninitialized uv_pipe_t structs for + * stdio. They will be initialized by uv_spawn. The user is reponsible for + * calling uv_close on them. + */ + uv_pipe_t* stdin_stream; + uv_pipe_t* stdout_stream; + uv_pipe_t* stderr_stream; +} uv_process_options_t; + +struct uv_process_s { + UV_HANDLE_FIELDS + uv_exit_cb exit_cb; + int pid; + UV_PROCESS_PRIVATE_FIELDS +}; + +/* Initializes uv_process_t and starts the process. */ +int uv_spawn(uv_process_t*, uv_process_options_t options); + +/* + * Kills the process with the specified signal. The user must still + * call uv_close on the process. + */ +int uv_process_kill(uv_process_t*, int signum); + /* * Most functions return boolean: 0 for success and -1 for failure. @@ -570,6 +614,7 @@ typedef struct { uint64_t idle_init; uint64_t async_init; uint64_t timer_init; + uint64_t process_init; } uv_counters_t; uv_counters_t* uv_counters(); diff --git a/deps/uv/msvs/libuv.vcxproj b/deps/uv/msvs/libuv.vcxproj index fbfca78941..8a9975d6d8 100644 --- a/deps/uv/msvs/libuv.vcxproj +++ b/deps/uv/msvs/libuv.vcxproj @@ -134,6 +134,7 @@ + diff --git a/deps/uv/src/uv-unix.c b/deps/uv/src/uv-unix.c index be52582f74..8d1c942029 100644 --- a/deps/uv/src/uv-unix.c +++ b/deps/uv/src/uv-unix.c @@ -51,6 +51,14 @@ #endif +# ifdef __APPLE__ +# include +# define environ (*_NSGetEnviron()) +# else +extern char **environ; +# endif + + static uv_err_t last_err; struct uv_ares_data_s { @@ -72,6 +80,8 @@ static int uv__stream_open(uv_stream_t*, int fd); static void uv__finish_close(uv_handle_t* handle); static uv_err_t uv_err_new(uv_handle_t* handle, int sys_error); +static int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); +static int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); static uv_write_t* uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); static void uv__stream_connect(uv_stream_t*); @@ -192,6 +202,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { uv_pipe_t* pipe; uv_async_t* async; uv_timer_t* timer; + uv_process_t* process; handle->close_cb = close_cb; @@ -244,6 +255,11 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { ev_io_stop(EV_DEFAULT_ &pipe->write_watcher); break; + case UV_PROCESS: + process = (uv_process_t*)handle; + ev_child_stop(EV_DEFAULT_UC_ &process->child_watcher); + break; + default: assert(0); } @@ -314,7 +330,8 @@ int uv_tcp_init(uv_tcp_t* tcp) { } -int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, int addrsize) { +static int uv__bind(uv_tcp_t* tcp, int domain, struct sockaddr* addr, + int addrsize) { int saved_errno; int status; int fd; @@ -360,7 +377,8 @@ int uv_tcp_bind(uv_tcp_t* tcp, struct sockaddr_in addr) { return -1; } - return uv__bind(tcp, AF_INET, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)); + return uv__bind(tcp, AF_INET, (struct sockaddr*)&addr, + sizeof(struct sockaddr_in)); } @@ -370,7 +388,8 @@ int uv_tcp_bind6(uv_tcp_t* tcp, struct sockaddr_in6 addr) { return -1; } - return uv__bind(tcp, AF_INET6, (struct sockaddr*)&addr, sizeof(struct sockaddr_in6)); + return uv__bind(tcp, AF_INET6, (struct sockaddr*)&addr, + sizeof(struct sockaddr_in6)); } @@ -429,12 +448,12 @@ void uv__server_io(EV_P_ ev_io* watcher, int revents) { return; } else { uv_err_new((uv_handle_t*)stream, errno); - stream->connection_cb((uv_handle_t*)stream, -1); + stream->connection_cb((uv_stream_t*)stream, -1); } } else { stream->accepted_fd = fd; - stream->connection_cb((uv_handle_t*)stream, 0); + stream->connection_cb((uv_stream_t*)stream, 0); if (stream->accepted_fd >= 0) { /* The user hasn't yet accepted called uv_accept() */ ev_io_stop(EV_DEFAULT_ &stream->read_watcher); @@ -445,7 +464,7 @@ void uv__server_io(EV_P_ ev_io* watcher, int revents) { } -int uv_accept(uv_handle_t* server, uv_stream_t* client) { +int uv_accept(uv_stream_t* server, uv_stream_t* client) { uv_stream_t* streamServer; uv_stream_t* streamClient; int saved_errno; @@ -458,7 +477,7 @@ int uv_accept(uv_handle_t* server, uv_stream_t* client) { streamClient = (uv_stream_t*)client; if (streamServer->accepted_fd < 0) { - uv_err_new(server, EAGAIN); + uv_err_new((uv_handle_t*)server, EAGAIN); goto out; } @@ -479,7 +498,20 @@ out: } -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +static int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { int r; int fd; @@ -565,6 +597,10 @@ void uv__finish_close(uv_handle_t* handle) { break; } + case UV_PROCESS: + assert(!ev_is_active(&((uv_process_t*)handle)->child_watcher)); + break; + default: assert(0); break; @@ -1544,7 +1580,7 @@ int64_t uv_timer_get_repeat(uv_timer_t* timer) { /* - * This is called once per second by ares_data.timer. It is used to + * This is called once per second by ares_data.timer. It is used to * constantly callback into c-ares for possibly processing timeouts. */ static void uv__ares_timeout(EV_P_ struct ev_timer* watcher, int revents) { @@ -1870,7 +1906,7 @@ out: } -int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { +static int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { int saved_errno; int status; @@ -1883,7 +1919,7 @@ int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { } assert(handle->fd >= 0); - if ((status = listen(handle->fd, SOMAXCONN)) == -1) { + if ((status = listen(handle->fd, backlog)) == -1) { uv_err_new((uv_handle_t*)handle, errno); } else { handle->connection_cb = cb; @@ -1984,7 +2020,7 @@ static void uv__pipe_accept(EV_P_ ev_io* watcher, int revents) { } } else { pipe->accepted_fd = sockfd; - pipe->connection_cb((uv_handle_t*)pipe, 0); + pipe->connection_cb((uv_stream_t*)pipe, 0); if (pipe->accepted_fd == sockfd) { /* The user hasn't yet accepted called uv_accept() */ ev_io_stop(EV_DEFAULT_ &pipe->read_watcher); @@ -2114,3 +2150,161 @@ size_t uv__strlcpy(char* dst, const char* src, size_t size) { return src - org; } + + +uv_stream_t* uv_std_handle(uv_std_type type) { + assert(0 && "implement me"); +} + + +static void uv__chld(EV_P_ ev_child* watcher, int revents) { + int status = watcher->rstatus; + int exit_status = 0; + int term_signal = 0; + uv_process_t *process = watcher->data; + + assert(&process->child_watcher == watcher); + assert(revents & EV_CHILD); + + ev_child_stop(EV_A_ &process->child_watcher); + + if (WIFEXITED(status)) { + exit_status = WEXITSTATUS(status); + } + + if (WIFSIGNALED(status)) { + term_signal = WTERMSIG(status); + } + + if (process->exit_cb) { + process->exit_cb(process, exit_status, term_signal); + } +} + + +int uv_spawn(uv_process_t* process, uv_process_options_t options) { + /* + * Save environ in the case that we get it clobbered + * by the child process. + */ + char** save_our_env = environ; + int stdin_pipe[2] = { -1, -1 }; + int stdout_pipe[2] = { -1, -1 }; + int stderr_pipe[2] = { -1, -1 }; + pid_t pid; + + uv__handle_init((uv_handle_t*)process, UV_PROCESS); + uv_counters()->process_init++; + + process->exit_cb = options.exit_cb; + + + if (options.stdin_stream && pipe(stdin_pipe) < 0) { + goto error; + } + + if (options.stdout_stream && pipe(stdout_pipe) < 0) { + goto error; + } + + if (options.stderr_stream && pipe(stderr_pipe) < 0) { + goto error; + } + + pid = fork(); + + if (pid == 0) { + if (stdin_pipe[0] >= 0) { + close(stdin_pipe[1]); + dup2(stdin_pipe[0], STDIN_FILENO); + } + + if (stdout_pipe[1] >= 0) { + close(stdout_pipe[0]); + dup2(stdout_pipe[1], STDOUT_FILENO); + } + + if (stderr_pipe[1] >= 0) { + close(stderr_pipe[0]); + dup2(stderr_pipe[1], STDERR_FILENO); + } + + if (options.cwd && chdir(options.cwd)) { + perror("chdir()"); + _exit(127); + } + + environ = options.env; + + execvp(options.file, options.args); + perror("execvp()"); + _exit(127); + /* Execution never reaches here. */ + } else if (pid == -1) { + /* Restore environment. */ + environ = save_our_env; + goto error; + } + + /* Parent. */ + + /* Restore environment. */ + environ = save_our_env; + + process->pid = pid; + + ev_child_init(&process->child_watcher, uv__chld, pid, 0); + ev_child_start(EV_DEFAULT_UC_ &process->child_watcher); + process->child_watcher.data = process; + + if (stdin_pipe[1] >= 0) { + assert(options.stdin_stream); + assert(stdin_pipe[0] >= 0); + close(stdin_pipe[0]); + uv__nonblock(stdin_pipe[1], 1); + uv_pipe_init(options.stdin_stream); + uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1]); + } + + if (stdout_pipe[0] >= 0) { + assert(options.stdout_stream); + assert(stdout_pipe[1] >= 0); + close(stdout_pipe[1]); + uv__nonblock(stdout_pipe[0], 1); + uv_pipe_init(options.stdout_stream); + uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0]); + } + + if (stderr_pipe[0] >= 0) { + assert(options.stderr_stream); + assert(stderr_pipe[1] >= 0); + close(stderr_pipe[1]); + uv__nonblock(stderr_pipe[0], 1); + uv_pipe_init(options.stderr_stream); + uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0]); + } + + return 0; + +error: + uv_err_new((uv_handle_t*)process, errno); + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[0]); + close(stdout_pipe[1]); + close(stderr_pipe[0]); + close(stderr_pipe[1]); + return -1; +} + + +int uv_process_kill(uv_process_t* process, int signum) { + int r = kill(process->pid, signum); + + if (r) { + uv_err_new((uv_handle_t*)process, errno); + return -1; + } else { + return 0; + } +} diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 87b9c37b98..efb05f85fe 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -102,6 +102,8 @@ extern uv_loop_t uv_main_loop_; #define UV_HANDLE_IPV6 0x2000 #define UV_HANDLE_PIPESERVER 0x4000 #define UV_HANDLE_READ_PENDING 0x8000 +#define UV_HANDLE_GIVEN_OS_HANDLE 0x10000 +#define UV_HANDLE_UV_ALLOCED 0x20000 void uv_want_endgame(uv_handle_t* handle); void uv_process_endgames(); @@ -144,6 +146,7 @@ void uv_winsock_startup(); void uv_tcp_endgame(uv_tcp_t* handle); +int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); @@ -159,9 +162,11 @@ void uv_process_tcp_connect_req(uv_tcp_t* handle, uv_connect_t* req); /* * Pipes */ +int uv_pipe_init_with_handle(uv_pipe_t* handle, HANDLE pipeHandle); void close_pipe(uv_pipe_t* handle, int* status, uv_err_t* err); void uv_pipe_endgame(uv_pipe_t* handle); +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client); int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index fe47b393c6..7c98ab0eff 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -46,6 +46,23 @@ int uv_pipe_init(uv_pipe_t* handle) { } +int uv_pipe_init_with_handle(uv_pipe_t* handle, HANDLE pipeHandle) { + int err = uv_pipe_init(handle); + + if (!err) { + /* + * At this point we don't know whether the pipe will be used as a client + * or a server. So, we assume that it will be a client until + * uv_listen is called. + */ + handle->handle = pipeHandle; + handle->flags |= UV_HANDLE_GIVEN_OS_HANDLE; + } + + return err; +} + + static int uv_set_pipe_handle(uv_pipe_t* handle, HANDLE pipeHandle) { DWORD mode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT; @@ -91,6 +108,10 @@ void uv_pipe_endgame(uv_pipe_t* handle) { handle->close_cb((uv_handle_t*)handle); } + if (handle->flags & UV_HANDLE_UV_ALLOCED) { + free(handle); + } + uv_unref(); } } @@ -407,7 +428,8 @@ int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client) { req->next_pending = NULL; req->pipeHandle = INVALID_HANDLE_VALUE; - if (!(server->flags & UV_HANDLE_CLOSING)) { + if (!(server->flags & UV_HANDLE_CLOSING) && + !(server->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { uv_pipe_queue_accept(server, req, FALSE); } @@ -416,15 +438,18 @@ int uv_pipe_accept(uv_pipe_t* server, uv_pipe_t* client) { /* Starts listening for connections for the given pipe. */ -int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { int i, errno; + uv_pipe_accept_t* req; + HANDLE pipeHandle; if (handle->flags & UV_HANDLE_BIND_ERROR) { LOOP->last_error = handle->error; return -1; } - if (!(handle->flags & UV_HANDLE_BOUND)) { + if (!(handle->flags & UV_HANDLE_BOUND) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { uv_set_error(UV_ENOTCONN, 0); return -1; } @@ -435,7 +460,8 @@ int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { return -1; } - if (!(handle->flags & UV_HANDLE_PIPESERVER)) { + if (!(handle->flags & UV_HANDLE_PIPESERVER) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { uv_set_error(UV_ENOTSUP, 0); return -1; } @@ -443,11 +469,30 @@ int uv_pipe_listen(uv_pipe_t* handle, uv_connection_cb cb) { handle->flags |= UV_HANDLE_LISTENING; handle->connection_cb = cb; - /* First pipe handle should have already been created in uv_pipe_bind */ - assert(handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); + if (handle->flags & UV_HANDLE_GIVEN_OS_HANDLE) { + handle->flags |= UV_HANDLE_PIPESERVER; + pipeHandle = handle->handle; + assert(pipeHandle != INVALID_HANDLE_VALUE); + req = &handle->accept_reqs[0]; + uv_req_init((uv_req_t*) req); + req->pipeHandle = pipeHandle; + req->type = UV_ACCEPT; + req->data = handle; + req->next_pending = NULL; - for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { - uv_pipe_queue_accept(handle, &handle->accept_reqs[i], i == 0); + if (uv_set_pipe_handle(handle, pipeHandle)) { + uv_set_sys_error(GetLastError()); + return -1; + } + + uv_pipe_queue_accept(handle, req, TRUE); + } else { + /* First pipe handle should have already been created in uv_pipe_bind */ + assert(handle->accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); + + for (i = 0; i < COUNTOF(handle->accept_reqs); i++) { + uv_pipe_queue_accept(handle, &handle->accept_reqs[i], i == 0); + } } return 0; @@ -678,14 +723,15 @@ void uv_process_pipe_accept_req(uv_pipe_t* handle, uv_req_t* raw_req) { handle->pending_accepts = req; if (handle->connection_cb) { - handle->connection_cb((uv_handle_t*)handle, 0); + handle->connection_cb((uv_stream_t*)handle, 0); } } else { if (req->pipeHandle != INVALID_HANDLE_VALUE) { CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; } - if (!(handle->flags & UV_HANDLE_CLOSING)) { + if (!(handle->flags & UV_HANDLE_CLOSING) && + !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { uv_pipe_queue_accept(handle, req, FALSE); } } diff --git a/deps/uv/src/win/stdio.c b/deps/uv/src/win/stdio.c new file mode 100644 index 0000000000..0333133972 --- /dev/null +++ b/deps/uv/src/win/stdio.c @@ -0,0 +1,75 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include + +#include "uv.h" +#include "../uv-common.h" +#include "internal.h" + + +static uv_pipe_t* uv_make_pipe_for_std_handle(HANDLE handle) { + uv_pipe_t* pipe = NULL; + + pipe = (uv_pipe_t*)malloc(sizeof(uv_pipe_t)); + if (!pipe) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (uv_pipe_init_with_handle(pipe, handle)) { + free(pipe); + return NULL; + } + + pipe->flags |= UV_HANDLE_UV_ALLOCED; + return pipe; +} + + +uv_stream_t* uv_std_handle(uv_std_type type) { + HANDLE handle; + + switch (type) { + case UV_STDIN: + handle = GetStdHandle(STD_INPUT_HANDLE); + if (handle == INVALID_HANDLE_VALUE) { + return NULL; + } + + /* Assume only named pipes for now. */ + return (uv_stream_t*)uv_make_pipe_for_std_handle(handle); + break; + + case UV_STDOUT: + return NULL; + break; + + case UV_STDERR: + return NULL; + break; + + default: + assert(0); + uv_set_error(UV_EINVAL, 0); + return NULL; + } +} diff --git a/deps/uv/src/win/stream.c b/deps/uv/src/win/stream.c index acd67a0c58..fcd88b8d67 100644 --- a/deps/uv/src/win/stream.c +++ b/deps/uv/src/win/stream.c @@ -48,27 +48,44 @@ void uv_connection_init(uv_stream_t* handle) { } -int uv_accept(uv_handle_t* server, uv_stream_t* client) { +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + switch (stream->type) { + case UV_TCP: + return uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + case UV_NAMED_PIPE: + return uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + default: + assert(0); + return -1; + } +} + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { assert(client->type == server->type); - if (server->type == UV_TCP) { - return uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); - } else if (server->type == UV_NAMED_PIPE) { - return uv_pipe_accept((uv_pipe_t*)server, (uv_pipe_t*)client); + switch (server->type) { + case UV_TCP: + return uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); + case UV_NAMED_PIPE: + return uv_pipe_accept((uv_pipe_t*)server, (uv_pipe_t*)client); + default: + assert(0); + return -1; } - - return -1; } int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { - if (handle->type == UV_TCP) { - return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); - } else if (handle->type == UV_NAMED_PIPE) { - return uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + switch (handle->type) { + case UV_TCP: + return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); + case UV_NAMED_PIPE: + return uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + default: + assert(0); + return -1; } - - return -1; } @@ -81,14 +98,16 @@ int uv_read_stop(uv_stream_t* handle) { int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt, uv_write_cb cb) { - if (handle->type == UV_TCP) { - return uv_tcp_write(req, (uv_tcp_t*) handle, bufs, bufcnt, cb); - } else if (handle->type == UV_NAMED_PIPE) { - return uv_pipe_write(req, (uv_pipe_t*) handle, bufs, bufcnt, cb); + switch (handle->type) { + case UV_TCP: + return uv_tcp_write(req, (uv_tcp_t*) handle, bufs, bufcnt, cb); + case UV_NAMED_PIPE: + return uv_pipe_write(req, (uv_pipe_t*) handle, bufs, bufcnt, cb); + default: + assert(0); + uv_set_sys_error(WSAEINVAL); + return -1; } - - uv_set_sys_error(WSAEINVAL); - return -1; } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index ace5353d20..c8d252c14f 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -842,7 +842,7 @@ void uv_process_tcp_accept_req(uv_tcp_t* handle, uv_req_t* req) { handle->flags &= ~UV_HANDLE_LISTENING; if (handle->connection_cb) { LOOP->last_error = req->error; - handle->connection_cb((uv_handle_t*)handle, -1); + handle->connection_cb((uv_stream_t*)handle, -1); } } } else if (req->error.code == UV_OK && @@ -853,7 +853,7 @@ void uv_process_tcp_accept_req(uv_tcp_t* handle, uv_req_t* req) { sizeof(handle->socket)) == 0) { /* Accept and SO_UPDATE_ACCEPT_CONTEXT were successful. */ if (handle->connection_cb) { - handle->connection_cb((uv_handle_t*)handle, 0); + handle->connection_cb((uv_stream_t*)handle, 0); } } else { /* Error related to accepted socket is ignored because the server */ diff --git a/deps/uv/test/benchmark-pump.c b/deps/uv/test/benchmark-pump.c index 1732e84ff5..4e7f1d7ace 100644 --- a/deps/uv/test/benchmark-pump.c +++ b/deps/uv/test/benchmark-pump.c @@ -47,7 +47,7 @@ static void buf_free(uv_buf_t uv_buf_t); static uv_tcp_t tcpServer; static uv_pipe_t pipeServer; -static uv_handle_t* server; +static uv_stream_t* server; static struct sockaddr_in listen_addr; static struct sockaddr_in connect_addr; @@ -143,7 +143,7 @@ void read_sockets_close_cb(uv_handle_t* handle) { */ if (uv_now() - start_time > 1000 && read_sockets == 0) { read_show_stats(); - uv_close(server, NULL); + uv_close((uv_handle_t*)server, NULL); } } @@ -266,7 +266,7 @@ static void maybe_connect_some() { } -static void connection_cb(uv_handle_t* s, int status) { +static void connection_cb(uv_stream_t* s, int status) { uv_stream_t* stream; int r; @@ -373,12 +373,12 @@ HELPER_IMPL(tcp_pump_server) { listen_addr = uv_ip4_addr("0.0.0.0", TEST_PORT); /* Server */ - server = (uv_handle_t*)&tcpServer; + server = (uv_stream_t*)&tcpServer; r = uv_tcp_init(&tcpServer); ASSERT(r == 0); r = uv_tcp_bind(&tcpServer, listen_addr); ASSERT(r == 0); - r = uv_tcp_listen(&tcpServer, MAX_WRITE_HANDLES, connection_cb); + r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); uv_run(); @@ -394,12 +394,12 @@ HELPER_IMPL(pipe_pump_server) { uv_init(); /* Server */ - server = (uv_handle_t*)&pipeServer; + server = (uv_stream_t*)&pipeServer; r = uv_pipe_init(&pipeServer); ASSERT(r == 0); r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); ASSERT(r == 0); - r = uv_pipe_listen(&pipeServer, connection_cb); + r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); uv_run(); diff --git a/deps/uv/test/dns-server.c b/deps/uv/test/dns-server.c index 1c6b78fd33..f1593528ed 100644 --- a/deps/uv/test/dns-server.c +++ b/deps/uv/test/dns-server.c @@ -55,7 +55,7 @@ static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); #define WRITE_BUF_LEN (64*1024) #define DNSREC_LEN (4) @@ -255,7 +255,7 @@ static uv_buf_t buf_alloc(uv_stream_t* handle, size_t suggested_size) { } -static void on_connection(uv_handle_t* server, int status) { +static void on_connection(uv_stream_t* server, int status) { dnshandle* handle; int r; @@ -302,7 +302,7 @@ static int dns_start(int port) { return 1; } - r = uv_tcp_listen(&server, 128, on_connection); + r = uv_listen((uv_stream_t*)&server, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); diff --git a/deps/uv/test/echo-server.c b/deps/uv/test/echo-server.c index 992c88bb2d..add2383983 100644 --- a/deps/uv/test/echo-server.c +++ b/deps/uv/test/echo-server.c @@ -39,7 +39,7 @@ static void after_write(uv_write_t* req, int status); static void after_read(uv_stream_t*, ssize_t nread, uv_buf_t buf); static void on_close(uv_handle_t* peer); static void on_server_close(uv_handle_t* handle); -static void on_connection(uv_handle_t*, int status); +static void on_connection(uv_stream_t*, int status); static void after_write(uv_write_t* req, int status) { @@ -123,7 +123,7 @@ static uv_buf_t echo_alloc(uv_stream_t* handle, size_t suggested_size) { } -static void on_connection(uv_handle_t* server, int status) { +static void on_connection(uv_stream_t* server, int status) { uv_stream_t* stream; int r; @@ -187,7 +187,7 @@ static int tcp4_echo_start(int port) { return 1; } - r = uv_tcp_listen(&tcpServer, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); @@ -220,7 +220,7 @@ static int tcp6_echo_start(int port) { return 0; } - r = uv_tcp_listen(&tcpServer, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); if (r) { /* TODO: Error codes */ fprintf(stderr, "Listen error\n"); @@ -249,7 +249,7 @@ static int pipe_echo_start(char* pipeName) { return 1; } - r = uv_pipe_listen(&pipeServer, on_connection); + r = uv_listen((uv_stream_t*)&pipeServer, SOMAXCONN, on_connection); if (r) { fprintf(stderr, "uv_pipe_listen: %s\n", uv_strerror(uv_last_error())); return 1; diff --git a/deps/uv/test/run-tests.c b/deps/uv/test/run-tests.c index 7ae6c6c7cb..c4a70dc93e 100644 --- a/deps/uv/test/run-tests.c +++ b/deps/uv/test/run-tests.c @@ -34,11 +34,24 @@ int main(int argc, char **argv) { + int i; + platform_init(argc, argv); switch (argc) { case 1: return run_tests(TEST_TIMEOUT, 0); - case 2: return run_test(argv[1], TEST_TIMEOUT, 0); + case 2: { + if (strcmp(argv[1], "spawn_helper1") == 0) { + return 1; + } + + if (strcmp(argv[1], "spawn_helper2") == 0) { + printf("hello world\n"); + return 1; + } + + return run_test(argv[1], TEST_TIMEOUT, 0); + } case 3: return run_test_part(argv[1], argv[2]); default: LOGF("Too many arguments.\n"); diff --git a/deps/uv/test/test-delayed-accept.c b/deps/uv/test/test-delayed-accept.c index 8e3dfc1461..10f041b2cd 100644 --- a/deps/uv/test/test-delayed-accept.c +++ b/deps/uv/test/test-delayed-accept.c @@ -63,7 +63,7 @@ static void do_accept(uv_timer_t* timer_handle, int status) { tcpcnt = uv_counters()->tcp_init; server = (uv_tcp_t*)timer_handle->data; - r = uv_accept((uv_handle_t*)server, (uv_stream_t*)accepted_handle); + r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); ASSERT(r == 0); ASSERT(uv_counters()->tcp_init == tcpcnt); @@ -83,7 +83,7 @@ static void do_accept(uv_timer_t* timer_handle, int status) { } -static void connection_cb(uv_handle_t* tcp, int status) { +static void connection_cb(uv_stream_t* tcp, int status) { int r; uv_timer_t* timer_handle; @@ -120,7 +120,7 @@ static void start_server() { r = uv_tcp_bind(server, addr); ASSERT(r == 0); - r = uv_tcp_listen(server, 128, connection_cb); + r = uv_listen((uv_stream_t*)server, 128, connection_cb); ASSERT(r == 0); } diff --git a/deps/uv/test/test-getsockname.c b/deps/uv/test/test-getsockname.c index 6e4f643661..8b783ec568 100644 --- a/deps/uv/test/test-getsockname.c +++ b/deps/uv/test/test-getsockname.c @@ -67,7 +67,7 @@ static void after_read(uv_stream_t* handle, ssize_t nread, uv_buf_t buf) { } -static void on_connection(uv_handle_t* server, int status) { +static void on_connection(uv_stream_t* server, int status) { struct sockaddr sockname; int namelen = sizeof(sockname); uv_handle_t* handle; @@ -141,7 +141,7 @@ static int tcp_listener(int port) { return 1; } - r = uv_tcp_listen(&tcpServer, 128, on_connection); + r = uv_listen((uv_stream_t*)&tcpServer, 128, on_connection); if (r) { fprintf(stderr, "Listen error\n"); return 1; diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index de9f1d3add..99d59e556f 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -63,6 +63,8 @@ TEST_DECLARE (gethostbyname) TEST_DECLARE (getsockname) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) +TEST_DECLARE (spawn_exit_code) +TEST_DECLARE (spawn_stdout) HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) HELPER_DECLARE (pipe_echo_server) @@ -140,6 +142,9 @@ TASK_LIST_START TEST_ENTRY (getsockname) + TEST_ENTRY (spawn_exit_code) + TEST_ENTRY (spawn_stdout) + #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) diff --git a/deps/uv/test/test-pipe-bind-error.c b/deps/uv/test/test-pipe-bind-error.c index 6faccbe202..69aaaa20cd 100644 --- a/deps/uv/test/test-pipe-bind-error.c +++ b/deps/uv/test/test-pipe-bind-error.c @@ -59,9 +59,9 @@ TEST_IMPL(pipe_bind_error_addrinuse) { ASSERT(uv_last_error().code == UV_EADDRINUSE); - r = uv_pipe_listen(&server1, NULL); + r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); ASSERT(r == 0); - r = uv_pipe_listen(&server2, NULL); + r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); ASSERT(r == -1); ASSERT(uv_last_error().code == UV_EADDRINUSE); @@ -133,7 +133,7 @@ TEST_IMPL(pipe_listen_without_bind) { r = uv_pipe_init(&server); ASSERT(r == 0); - r = uv_pipe_listen(&server, NULL); + r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); ASSERT(r == -1); ASSERT(uv_last_error().code == UV_ENOTCONN); diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c new file mode 100644 index 0000000000..9449af7cb9 --- /dev/null +++ b/deps/uv/test/test-spawn.c @@ -0,0 +1,134 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include +#include + +static int close_cb_called; +static int exit_cb_called; +static uv_process_t process; +static uv_process_options_t options = { 0 }; +static char exepath[1024]; +static size_t exepath_size = 1024; +static char* args[3]; + +#define OUTPUT_SIZE 1024 +static char output[OUTPUT_SIZE]; +static int output_used; + + +static void close_cb(uv_handle_t* handle) { + printf("close_cb\n"); + close_cb_called++; +} + + +static void exit_cb(uv_process_t* process, int exit_status, int term_signal) { + printf("exit_cb\n"); + exit_cb_called++; + ASSERT(exit_status == 1); + ASSERT(term_signal == 0); + uv_close((uv_handle_t*)process, close_cb); +} + + +uv_buf_t on_alloc(uv_stream_t* tcp, size_t suggested_size) { + uv_buf_t buf; + buf.base = output + output_used; + buf.len = OUTPUT_SIZE - output_used; + return buf; +} + + +void on_read(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + uv_err_t err = uv_last_error(); + + if (nread > 0) { + output_used += nread; + } else if (nread < 0) { + if (err.code == UV_EOF) { + uv_close((uv_handle_t*)tcp, close_cb); + } + } +} + + +static void init_process_options(char* test) { + /* Note spawn_helper1 defined in test/run-tests.c */ + int r = uv_exepath(exepath, &exepath_size); + ASSERT(r == 0); + exepath[exepath_size] = '\0'; + args[0] = exepath; + args[1] = test; + args[2] = NULL; + options.file = exepath; + options.args = args; + options.exit_cb = exit_cb; +} + + +TEST_IMPL(spawn_exit_code) { + int r; + + uv_init(); + + init_process_options("spawn_helper1"); + + r = uv_spawn(&process, options); + ASSERT(r == 0); + + r = uv_run(); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 1); + + return 0; +} + + +TEST_IMPL(spawn_stdout) { + int r; + uv_pipe_t out; + + uv_init(); + + init_process_options("spawn_helper2"); + options.stdout_stream = &out; + + r = uv_spawn(&process, options); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); + ASSERT(r == 0); + + r = uv_run(); + ASSERT(r == 0); + + ASSERT(exit_cb_called == 1); + ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + ASSERT(strcmp("hello world\n", output) == 0); + + return 0; +} + diff --git a/deps/uv/test/test-tcp-bind-error.c b/deps/uv/test/test-tcp-bind-error.c index 9034438dfb..b466ef8b4f 100644 --- a/deps/uv/test/test-tcp-bind-error.c +++ b/deps/uv/test/test-tcp-bind-error.c @@ -51,9 +51,9 @@ TEST_IMPL(tcp_bind_error_addrinuse) { r = uv_tcp_bind(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); ASSERT(uv_last_error().code == UV_EADDRINUSE); @@ -197,7 +197,7 @@ TEST_IMPL(tcp_listen_without_bind) { uv_init(); r = uv_tcp_init(&server); ASSERT(r == 0); - r = uv_tcp_listen(&server, 128, NULL); + r = uv_listen((uv_stream_t*)&server, 128, NULL); ASSERT(r == 0); return 0; diff --git a/deps/uv/test/test-tcp-bind6-error.c b/deps/uv/test/test-tcp-bind6-error.c index d2127858ee..cf283fa000 100644 --- a/deps/uv/test/test-tcp-bind6-error.c +++ b/deps/uv/test/test-tcp-bind6-error.c @@ -51,9 +51,9 @@ TEST_IMPL(tcp_bind6_error_addrinuse) { r = uv_tcp_bind6(&server2, addr); ASSERT(r == 0); - r = uv_tcp_listen(&server1, 128, NULL); + r = uv_listen((uv_stream_t*)&server1, 128, NULL); ASSERT(r == 0); - r = uv_tcp_listen(&server2, 128, NULL); + r = uv_listen((uv_stream_t*)&server2, 128, NULL); ASSERT(r == -1); ASSERT(uv_last_error().code == UV_EADDRINUSE);