diff --git a/Makefile.am b/Makefile.am index cbc1807c..97847ce2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -142,6 +142,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-getaddrinfo.c \ test/test-getnameinfo.c \ test/test-getsockname.c \ + test/test-handle-fileno.c \ test/test-hrtime.c \ test/test-idle.c \ test/test-ip4-addr.c \ diff --git a/include/uv-unix.h b/include/uv-unix.h index f66e4941..fe74f327 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -125,6 +125,7 @@ typedef struct uv_buf_t { typedef int uv_file; typedef int uv_os_sock_t; +typedef int uv_os_fd_t; #define UV_ONCE_INIT PTHREAD_ONCE_INIT diff --git a/include/uv-win.h b/include/uv-win.h index 48fa020a..eea48cfc 100644 --- a/include/uv-win.h +++ b/include/uv-win.h @@ -229,8 +229,8 @@ typedef struct uv_buf_t { } uv_buf_t; typedef int uv_file; - typedef SOCKET uv_os_sock_t; +typedef HANDLE uv_os_fd_t; typedef HANDLE uv_thread_t; diff --git a/include/uv.h b/include/uv.h index 730aa151..b472a89c 100644 --- a/include/uv.h +++ b/include/uv.h @@ -615,6 +615,8 @@ UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb); */ UV_EXTERN int uv_recv_buffer_size(uv_handle_t* handle, int* value); +UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); + /* * Returns or sets the size of the send buffer that the operating * system uses for the socket. diff --git a/src/unix/core.c b/src/unix/core.c index aaced4c9..9dcc3935 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -662,6 +662,36 @@ void uv_disable_stdio_inheritance(void) { } +int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { + int fd_out; + + switch (handle->type) { + case UV_TCP: + case UV_NAMED_PIPE: + case UV_TTY: + fd_out = uv__stream_fd((uv_stream_t*) handle); + break; + + case UV_UDP: + fd_out = ((uv_udp_t *) handle)->io_watcher.fd; + break; + + case UV_POLL: + fd_out = ((uv_poll_t *) handle)->io_watcher.fd; + break; + + default: + return -EINVAL; + } + + if (uv__is_closing(handle) || fd_out == -1) + return -EBADF; + + *fd = fd_out; + return 0; +} + + static void uv__run_pending(uv_loop_t* loop) { QUEUE* q; uv__io_t* w; diff --git a/src/win/core.c b/src/win/core.c index 5c3c6390..67de90d6 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -429,6 +429,43 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { return r; } + +int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { + uv_os_fd_t fd_out; + + switch (handle->type) { + case UV_TCP: + fd_out = (uv_os_fd_t)((uv_tcp_t*) handle)->socket; + break; + + case UV_NAMED_PIPE: + fd_out = ((uv_pipe_t*) handle)->handle; + break; + + case UV_TTY: + fd_out = ((uv_tty_t*) handle)->handle; + break; + + case UV_UDP: + fd_out = (uv_os_fd_t)((uv_udp_t*) handle)->socket; + break; + + case UV_POLL: + fd_out = (uv_os_fd_t)((uv_poll_t*) handle)->socket; + break; + + default: + return UV_EINVAL; + } + + if (uv_is_closing(handle) || fd_out == INVALID_HANDLE_VALUE) + return UV_EBADF; + + *fd = fd_out; + return 0; +} + + int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { int r; int len; diff --git a/test/test-handle-fileno.c b/test/test-handle-fileno.c new file mode 100644 index 00000000..df5e984a --- /dev/null +++ b/test/test-handle-fileno.c @@ -0,0 +1,120 @@ +/* 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" + + +static int get_tty_fd(void) { + /* Make sure we have an FD that refers to a tty */ +#ifdef _WIN32 + HANDLE handle; + handle = CreateFileA("conout$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (handle == INVALID_HANDLE_VALUE) + return -1; + return _open_osfhandle((intptr_t) handle, 0); +#else /* unix */ + return open("/dev/tty", O_RDONLY, 0); +#endif +} + + +TEST_IMPL(handle_fileno) { + int r; + int tty_fd; + struct sockaddr_in addr; + uv_os_fd_t fd; + uv_tcp_t tcp; + uv_udp_t udp; + uv_pipe_t pipe; + uv_tty_t tty; + uv_idle_t idle; + uv_loop_t* loop; + + loop = uv_default_loop(); + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_idle_init(loop, &idle); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &idle, &fd); + ASSERT(r == UV_EINVAL); + uv_close((uv_handle_t*) &idle, NULL); + + r = uv_tcp_init(loop, &tcp); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == UV_EBADF); + r = uv_tcp_bind(&tcp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &tcp, NULL); + r = uv_fileno((uv_handle_t*) &tcp, &fd); + ASSERT(r == UV_EBADF); + + r = uv_udp_init(loop, &udp); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == UV_EBADF); + r = uv_udp_bind(&udp, (const struct sockaddr*) &addr, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &udp, NULL); + r = uv_fileno((uv_handle_t*) &udp, &fd); + ASSERT(r == UV_EBADF); + + r = uv_pipe_init(loop, &pipe, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == UV_EBADF); + r = uv_pipe_bind(&pipe, TEST_PIPENAME); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &pipe, NULL); + r = uv_fileno((uv_handle_t*) &pipe, &fd); + ASSERT(r == UV_EBADF); + + tty_fd = get_tty_fd(); + if (tty_fd < 0) { + LOGF("Cannot open a TTY fd"); + } else { + r = uv_tty_init(loop, &tty, tty_fd, 0); + ASSERT(r == 0); + r = uv_fileno((uv_handle_t*) &tty, &fd); + ASSERT(r == 0); + uv_close((uv_handle_t*) &tty, NULL); + r = uv_fileno((uv_handle_t*) &tty, &fd); + ASSERT(r == UV_EBADF); + } + + uv_run(loop, UV_RUN_DEFAULT); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index cb069668..657ac362 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -172,6 +172,7 @@ TEST_DECLARE (get_currentexe) TEST_DECLARE (process_title) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) +TEST_DECLARE (handle_fileno) TEST_DECLARE (hrtime) TEST_DECLARE (getaddrinfo_fail) TEST_DECLARE (getaddrinfo_basic) @@ -503,6 +504,8 @@ TASK_LIST_START TEST_ENTRY (get_loadavg) + TEST_ENTRY (handle_fileno) + TEST_ENTRY (hrtime) TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) diff --git a/uv.gyp b/uv.gyp index 073dfce8..eabeab2e 100644 --- a/uv.gyp +++ b/uv.gyp @@ -321,6 +321,7 @@ 'test/test-getaddrinfo.c', 'test/test-getnameinfo.c', 'test/test-getsockname.c', + 'test/test-handle-fileno.c', 'test/test-hrtime.c', 'test/test-idle.c', 'test/test-ip6-addr.c',