mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
uv: upgrade to v0.11.15
This commit is contained in:
1 parent
aef652dc11
commit
1fef66ffd4
56 files changed
+3018
-346
No files matched your search
Vendored
+2
@@ -97,3 +97,5 @@ Luca Bruno <lucab@debian.org>
|
||||
Reini Urban <rurban@cpanel.net>
|
||||
Maks Naumov <maksqwe1@ukr.net>
|
||||
Sean Farrell <sean.farrell@rioki.org>
|
||||
Chris Bank <cbank@adobe.com>
|
||||
Geert Jansen <geertj@gmail.com>
|
||||
Vendored
+69
-1
@@ -1,4 +1,72 @@
|
||||
2013.10.30, Version 0.11.14 (Unstable)
|
||||
2013.11.21, Version 0.11.15 (Unstable)
|
||||
|
||||
Changes since version 0.11.14:
|
||||
|
||||
* fsevents: report errors to user (Fedor Indutny)
|
||||
|
||||
* include: UV_FS_EVENT_RECURSIVE is a flag (Fedor Indutny)
|
||||
|
||||
* linux: use CLOCK_MONOTONIC_COARSE if available (Ben Noordhuis)
|
||||
|
||||
* build: make systemtap probes work with gyp build (Ben Noordhuis)
|
||||
|
||||
* unix: update events from pevents between polls (Fedor Indutny)
|
||||
|
||||
* fsevents: support japaneese characters in path (Chris Bank)
|
||||
|
||||
* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis)
|
||||
|
||||
* queue: strengthen type checks (Ben Noordhuis)
|
||||
|
||||
* include: remove uv_strlcat() and uv_strlcpy() (Ben Noordhuis)
|
||||
|
||||
* build: fix windows smp build with gyp (Geert Jansen)
|
||||
|
||||
* unix: return exec errors from uv_spawn, not async (Alex Crichton)
|
||||
|
||||
* fsevents: use native character encoding file paths (Ben Noordhuis)
|
||||
|
||||
* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis)
|
||||
|
||||
* windows: use _snwprintf(), not swprintf() (Ben Noordhuis)
|
||||
|
||||
* fsevents: use FlagNoDefer for FSEventStreamCreate (Fedor Indutny)
|
||||
|
||||
* unix: fix reopened fd bug (Fedor Indutny)
|
||||
|
||||
* core: fix fake watcher list and count preservation (Fedor Indutny)
|
||||
|
||||
* unix: set close-on-exec flag on received fds (Ben Noordhuis)
|
||||
|
||||
* netbsd, openbsd: enable futimes() wrapper (Ben Noordhuis)
|
||||
|
||||
* unix: nicer error message when kqueue() fails (Ben Noordhuis)
|
||||
|
||||
* samples: add socks5 proxy sample application (Ben Noordhuis)
|
||||
|
||||
|
||||
2013.11.13, Version 0.10.19 (Stable), 33959f7524090b8d2c6c41e2400ca77e31755059
|
||||
|
||||
Changes since version 0.10.18:
|
||||
|
||||
* darwin: avoid calling GetCurrentProcess (Fedor Indutny)
|
||||
|
||||
* unix: update events from pevents between polls (Fedor Indutny)
|
||||
|
||||
* fsevents: support japaneese characters in path (Chris Bank)
|
||||
|
||||
* linux: don't turn on SO_REUSEPORT socket option (Ben Noordhuis)
|
||||
|
||||
* build: fix windows smp build with gyp (Geert Jansen)
|
||||
|
||||
* linux: handle EPOLLHUP without EPOLLIN/EPOLLOUT (Ben Noordhuis)
|
||||
|
||||
* unix: fix reopened fd bug (Fedor Indutny)
|
||||
|
||||
* core: fix fake watcher list and count preservation (Fedor Indutny)
|
||||
|
||||
|
||||
2013.10.30, Version 0.11.14 (Unstable), d7a6482f45c1b4eb4a853dbe1a9ce8090a35633a
|
||||
|
||||
Changes since version 0.11.13:
|
||||
|
||||
|
||||
Vendored
+4
-2
@@ -118,6 +118,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-barrier.c \
|
||||
test/test-callback-order.c \
|
||||
test/test-callback-stack.c \
|
||||
test/test-close-fd.c \
|
||||
test/test-close-order.c \
|
||||
test/test-condvar.c \
|
||||
test/test-connection-fail.c \
|
||||
@@ -166,6 +167,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-stdio-over-pipes.c \
|
||||
test/test-tcp-bind-error.c \
|
||||
test/test-tcp-bind6-error.c \
|
||||
test/test-tcp-close-accept.c \
|
||||
test/test-tcp-close-while-connecting.c \
|
||||
test/test-tcp-close.c \
|
||||
test/test-tcp-connect-error-after-write.c \
|
||||
@@ -193,8 +195,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-udp-open.c \
|
||||
test/test-udp-options.c \
|
||||
test/test-udp-send-and-recv.c \
|
||||
test/test-util.c \
|
||||
test/test-walk-handles.c
|
||||
test/test-walk-handles.c \
|
||||
test/test-watcher-cross-stop.c
|
||||
test_run_tests_LDADD = libuv.la
|
||||
|
||||
if WINNT
|
||||
|
||||
Vendored
+2
-2
@@ -83,14 +83,14 @@ project tree manually:
|
||||
|
||||
Run:
|
||||
|
||||
$ ./gyp_uv -f make
|
||||
$ ./gyp_uv.py -f make
|
||||
$ make -C out
|
||||
|
||||
### OS X
|
||||
|
||||
Run:
|
||||
|
||||
$ ./gyp_uv -f xcode
|
||||
$ ./gyp_uv.py -f xcode
|
||||
$ xcodebuild -project uv.xcodeproj -configuration Release -target All
|
||||
|
||||
### Android
|
||||
|
||||
Vendored
+1
-1
@@ -16,5 +16,5 @@ export PLATFORM=android
|
||||
|
||||
if [ $2 -a $2 == 'gyp' ]
|
||||
then
|
||||
./gyp_uv -Dtarget_arch=arm -DOS=android
|
||||
./gyp_uv.py -Dtarget_arch=arm -DOS=android
|
||||
fi
|
||||
Vendored
+2
-1
@@ -132,6 +132,7 @@ test/test-stdio-over-pipes.c
|
||||
test/test-tcp-bind-error.c
|
||||
test/test-tcp-bind6-error.c
|
||||
test/test-tcp-close-while-connecting.c
|
||||
test/test-tcp-close-accept.c
|
||||
test/test-tcp-close.c
|
||||
test/test-tcp-connect-error-after-write.c
|
||||
test/test-tcp-connect-error.c
|
||||
@@ -158,8 +159,8 @@ test/test-udp-multicast-ttl.c
|
||||
test/test-udp-open.c
|
||||
test/test-udp-options.c
|
||||
test/test-udp-send-and-recv.c
|
||||
test/test-util.c
|
||||
test/test-walk-handles.c
|
||||
test/test-watcher-cross-stop.c
|
||||
"
|
||||
|
||||
case `uname -s` in
|
||||
|
||||
Vendored
+1
-1
@@ -13,7 +13,7 @@
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
AC_INIT([libuv], [0.11.14], [https://github.com/joyent/libuv/issues])
|
||||
AC_INIT([libuv], [0.11.15], [https://github.com/joyent/libuv/issues])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
m4_include([m4/libuv-extra-automake-flags.m4])
|
||||
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects] UV_EXTRA_AUTOMAKE_FLAGS)
|
||||
|
||||
File renamed without changes.
Vendored
+2
-2
@@ -47,10 +47,10 @@
|
||||
char* realpath; \
|
||||
int realpath_len; \
|
||||
int cf_flags; \
|
||||
void* cf_event; \
|
||||
uv_async_t* cf_cb; \
|
||||
void* cf_events[2]; \
|
||||
void* cf_member[2]; \
|
||||
uv_sem_t _cf_reserved; \
|
||||
int cf_error; \
|
||||
uv_mutex_t cf_mutex; \
|
||||
|
||||
#define UV_STREAM_PRIVATE_PLATFORM_FIELDS \
|
||||
|
||||
Vendored
-1
@@ -287,7 +287,6 @@ typedef struct {
|
||||
|
||||
#define UV_PROCESS_PRIVATE_FIELDS \
|
||||
void* queue[2]; \
|
||||
int errorno; \
|
||||
int status; \
|
||||
|
||||
#define UV_FS_PRIVATE_FIELDS \
|
||||
|
||||
Vendored
-1
@@ -533,7 +533,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
|
||||
UV_REQ_FIELDS \
|
||||
} exit_req; \
|
||||
BYTE* child_stdio_buffer; \
|
||||
int spawn_error; \
|
||||
int exit_signal; \
|
||||
HANDLE wait_handle; \
|
||||
HANDLE process_handle; \
|
||||
|
||||
Vendored
+13
-18
@@ -567,21 +567,6 @@ UV_EXTERN void uv_close(uv_handle_t* handle, uv_close_cb close_cb);
|
||||
UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len);
|
||||
|
||||
|
||||
/*
|
||||
* Utility function. Copies up to `size` characters from `src` to `dst`
|
||||
* and ensures that `dst` is properly NUL terminated unless `size` is zero.
|
||||
*/
|
||||
UV_EXTERN size_t uv_strlcpy(char* dst, const char* src, size_t size);
|
||||
|
||||
/*
|
||||
* Utility function. Appends `src` to `dst` and ensures that `dst` is
|
||||
* properly NUL terminated unless `size` is zero or `dst` does not
|
||||
* contain a NUL byte. `size` is the total length of `dst` so at most
|
||||
* `size - strlen(dst) - 1` characters will be copied from `src`.
|
||||
*/
|
||||
UV_EXTERN size_t uv_strlcat(char* dst, const char* src, size_t size);
|
||||
|
||||
|
||||
#define UV_STREAM_FIELDS \
|
||||
/* number of bytes queued for writing */ \
|
||||
size_t write_queue_size; \
|
||||
@@ -1504,7 +1489,17 @@ struct uv_process_s {
|
||||
UV_PROCESS_PRIVATE_FIELDS
|
||||
};
|
||||
|
||||
/* Initializes uv_process_t and starts the process. */
|
||||
/*
|
||||
* Initializes the uv_process_t and starts the process. If the process is
|
||||
* successfully spawned, then this function will return 0. Otherwise, the
|
||||
* negative error code corresponding to the reason it couldn't spawn is
|
||||
* returned.
|
||||
*
|
||||
* Possible reasons for failing to spawn would include (but not be limited to)
|
||||
* the file to execute not existing, not having permissions to use the setuid or
|
||||
* setgid specified, or not having enough memory to allocate for the new
|
||||
* process.
|
||||
*/
|
||||
UV_EXTERN int uv_spawn(uv_loop_t* loop,
|
||||
uv_process_t* handle,
|
||||
const uv_process_options_t* options);
|
||||
@@ -1873,7 +1868,7 @@ enum uv_fs_event_flags {
|
||||
* flag does not affect individual files watched.
|
||||
* This flag is currently not implemented yet on any backend.
|
||||
*/
|
||||
UV_FS_EVENT_WATCH_ENTRY = 1,
|
||||
UV_FS_EVENT_WATCH_ENTRY = 1,
|
||||
|
||||
/*
|
||||
* By default uv_fs_event will try to use a kernel interface such as inotify
|
||||
@@ -1889,7 +1884,7 @@ enum uv_fs_event_flags {
|
||||
* (is ignoring) changes in it's subdirectories.
|
||||
* This flag will override this behaviour on platforms that support it.
|
||||
*/
|
||||
UV_FS_EVENT_RECURSIVE = 3
|
||||
UV_FS_EVENT_RECURSIVE = 4
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# Copyright StrongLoop, Inc. 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.
|
||||
|
||||
*.mk
|
||||
*.Makefile
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright StrongLoop, Inc. 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.
|
||||
|
||||
/build/
|
||||
Vendored
+53
@@ -0,0 +1,53 @@
|
||||
Files: *
|
||||
========
|
||||
|
||||
Copyright StrongLoop, Inc. 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.
|
||||
|
||||
|
||||
Files: getopt.c
|
||||
===============
|
||||
|
||||
Copyright (c) 1987, 1993, 1994
|
||||
The Regents of the University of California. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the University nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
# Copyright StrongLoop, Inc. 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.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'dependencies': ['../../uv.gyp:libuv'],
|
||||
'target_name': 's5-proxy',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
'client.c',
|
||||
'defs.h',
|
||||
'main.c',
|
||||
's5.c',
|
||||
's5.h',
|
||||
'server.c',
|
||||
'util.c',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'defines': ['HAVE_UNISTD_H=0'],
|
||||
'sources': ['getopt.c']
|
||||
}, {
|
||||
'defines': ['HAVE_UNISTD_H=1']
|
||||
}]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+737
@@ -0,0 +1,737 @@
|
||||
/* Copyright StrongLoop, Inc. 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 "defs.h"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* A connection is modeled as an abstraction on top of two simple state
|
||||
* machines, one for reading and one for writing. Either state machine
|
||||
* is, when active, in one of three states: busy, done or stop; the fourth
|
||||
* and final state, dead, is an end state and only relevant when shutting
|
||||
* down the connection. A short overview:
|
||||
*
|
||||
* busy done stop
|
||||
* ----------|---------------------------|--------------------|------|
|
||||
* readable | waiting for incoming data | have incoming data | idle |
|
||||
* writable | busy writing out data | completed write | idle |
|
||||
*
|
||||
* We could remove the done state from the writable state machine. For our
|
||||
* purposes, it's functionally equivalent to the stop state.
|
||||
*
|
||||
* When the connection with upstream has been established, the client_ctx
|
||||
* moves into a state where incoming data from the client is sent upstream
|
||||
* and vice versa, incoming data from upstream is sent to the client. In
|
||||
* other words, we're just piping data back and forth. See conn_cycle()
|
||||
* for details.
|
||||
*
|
||||
* An interesting deviation from libuv's I/O model is that reads are discrete
|
||||
* rather than continuous events. In layman's terms, when a read operation
|
||||
* completes, the connection stops reading until further notice.
|
||||
*
|
||||
* The rationale for this approach is that we have to wait until the data
|
||||
* has been sent out again before we can reuse the read buffer.
|
||||
*
|
||||
* It also pleasingly unifies with the request model that libuv uses for
|
||||
* writes and everything else; libuv may switch to a request model for
|
||||
* reads in the future.
|
||||
*/
|
||||
enum conn_state {
|
||||
c_busy, /* Busy; waiting for incoming data or for a write to complete. */
|
||||
c_done, /* Done; read incoming data or write finished. */
|
||||
c_stop, /* Stopped. */
|
||||
c_dead
|
||||
};
|
||||
|
||||
/* Session states. */
|
||||
enum sess_state {
|
||||
s_handshake, /* Wait for client handshake. */
|
||||
s_handshake_auth, /* Wait for client authentication data. */
|
||||
s_req_start, /* Start waiting for request data. */
|
||||
s_req_parse, /* Wait for request data. */
|
||||
s_req_lookup, /* Wait for upstream hostname DNS lookup to complete. */
|
||||
s_req_connect, /* Wait for uv_tcp_connect() to complete. */
|
||||
s_proxy_start, /* Connected. Start piping data. */
|
||||
s_proxy, /* Connected. Pipe data back and forth. */
|
||||
s_kill, /* Tear down session. */
|
||||
s_almost_dead_0, /* Waiting for finalizers to complete. */
|
||||
s_almost_dead_1, /* Waiting for finalizers to complete. */
|
||||
s_almost_dead_2, /* Waiting for finalizers to complete. */
|
||||
s_almost_dead_3, /* Waiting for finalizers to complete. */
|
||||
s_almost_dead_4, /* Waiting for finalizers to complete. */
|
||||
s_dead /* Dead. Safe to free now. */
|
||||
};
|
||||
|
||||
static void do_next(client_ctx *cx);
|
||||
static int do_handshake(client_ctx *cx);
|
||||
static int do_handshake_auth(client_ctx *cx);
|
||||
static int do_req_start(client_ctx *cx);
|
||||
static int do_req_parse(client_ctx *cx);
|
||||
static int do_req_lookup(client_ctx *cx);
|
||||
static int do_req_connect_start(client_ctx *cx);
|
||||
static int do_req_connect(client_ctx *cx);
|
||||
static int do_proxy_start(client_ctx *cx);
|
||||
static int do_proxy(client_ctx *cx);
|
||||
static int do_kill(client_ctx *cx);
|
||||
static int do_almost_dead(client_ctx *cx);
|
||||
static int conn_cycle(const char *who, conn *a, conn *b);
|
||||
static void conn_timer_reset(conn *c);
|
||||
static void conn_timer_expire(uv_timer_t *handle, int status);
|
||||
static void conn_getaddrinfo(conn *c, const char *hostname);
|
||||
static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
|
||||
int status,
|
||||
struct addrinfo *ai);
|
||||
static int conn_connect(conn *c);
|
||||
static void conn_connect_done(uv_connect_t *req, int status);
|
||||
static void conn_read(conn *c);
|
||||
static void conn_read_done(uv_stream_t *handle,
|
||||
ssize_t nread,
|
||||
const uv_buf_t *buf);
|
||||
static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf);
|
||||
static void conn_write(conn *c, const void *data, unsigned int len);
|
||||
static void conn_write_done(uv_write_t *req, int status);
|
||||
static void conn_close(conn *c);
|
||||
static void conn_close_done(uv_handle_t *handle);
|
||||
|
||||
/* |incoming| has been initialized by server.c when this is called. */
|
||||
void client_finish_init(server_ctx *sx, client_ctx *cx) {
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
|
||||
cx->sx = sx;
|
||||
cx->state = s_handshake;
|
||||
s5_init(&cx->parser);
|
||||
|
||||
incoming = &cx->incoming;
|
||||
incoming->client = cx;
|
||||
incoming->result = 0;
|
||||
incoming->rdstate = c_stop;
|
||||
incoming->wrstate = c_stop;
|
||||
incoming->idle_timeout = sx->idle_timeout;
|
||||
CHECK(0 == uv_timer_init(sx->loop, &incoming->timer_handle));
|
||||
|
||||
outgoing = &cx->outgoing;
|
||||
outgoing->client = cx;
|
||||
outgoing->result = 0;
|
||||
outgoing->rdstate = c_stop;
|
||||
outgoing->wrstate = c_stop;
|
||||
outgoing->idle_timeout = sx->idle_timeout;
|
||||
CHECK(0 == uv_tcp_init(cx->sx->loop, &outgoing->handle.tcp));
|
||||
CHECK(0 == uv_timer_init(cx->sx->loop, &outgoing->timer_handle));
|
||||
|
||||
/* Wait for the initial packet. */
|
||||
conn_read(incoming);
|
||||
}
|
||||
|
||||
/* This is the core state machine that drives the client <-> upstream proxy.
|
||||
* We move through the initial handshake and authentication steps first and
|
||||
* end up (if all goes well) in the proxy state where we're just proxying
|
||||
* data between the client and upstream.
|
||||
*/
|
||||
static void do_next(client_ctx *cx) {
|
||||
int new_state;
|
||||
|
||||
ASSERT(cx->state != s_dead);
|
||||
switch (cx->state) {
|
||||
case s_handshake:
|
||||
new_state = do_handshake(cx);
|
||||
break;
|
||||
case s_handshake_auth:
|
||||
new_state = do_handshake_auth(cx);
|
||||
break;
|
||||
case s_req_start:
|
||||
new_state = do_req_start(cx);
|
||||
break;
|
||||
case s_req_parse:
|
||||
new_state = do_req_parse(cx);
|
||||
break;
|
||||
case s_req_lookup:
|
||||
new_state = do_req_lookup(cx);
|
||||
break;
|
||||
case s_req_connect:
|
||||
new_state = do_req_connect(cx);
|
||||
break;
|
||||
case s_proxy_start:
|
||||
new_state = do_proxy_start(cx);
|
||||
break;
|
||||
case s_proxy:
|
||||
new_state = do_proxy(cx);
|
||||
break;
|
||||
case s_kill:
|
||||
new_state = do_kill(cx);
|
||||
break;
|
||||
case s_almost_dead_0:
|
||||
case s_almost_dead_1:
|
||||
case s_almost_dead_2:
|
||||
case s_almost_dead_3:
|
||||
case s_almost_dead_4:
|
||||
new_state = do_almost_dead(cx);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
cx->state = new_state;
|
||||
|
||||
if (cx->state == s_dead) {
|
||||
if (DEBUG_CHECKS) {
|
||||
memset(cx, -1, sizeof(*cx));
|
||||
}
|
||||
free(cx);
|
||||
}
|
||||
}
|
||||
|
||||
static int do_handshake(client_ctx *cx) {
|
||||
unsigned int methods;
|
||||
conn *incoming;
|
||||
s5_ctx *parser;
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
int err;
|
||||
|
||||
parser = &cx->parser;
|
||||
incoming = &cx->incoming;
|
||||
ASSERT(incoming->rdstate == c_done);
|
||||
ASSERT(incoming->wrstate == c_stop);
|
||||
incoming->rdstate = c_stop;
|
||||
|
||||
if (incoming->result < 0) {
|
||||
pr_err("read error: %s", uv_strerror(incoming->result));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
data = (uint8_t *) incoming->t.buf;
|
||||
size = (size_t) incoming->result;
|
||||
err = s5_parse(parser, &data, &size);
|
||||
if (err == s5_ok) {
|
||||
conn_read(incoming);
|
||||
return s_handshake; /* Need more data. */
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
/* Could allow a round-trip saving shortcut here if the requested auth
|
||||
* method is S5_AUTH_NONE (provided unauthenticated traffic is allowed.)
|
||||
* Requires client support however.
|
||||
*/
|
||||
pr_err("junk in handshake");
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
if (err != s5_auth_select) {
|
||||
pr_err("handshake error: %s", s5_strerror(err));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
methods = s5_auth_methods(parser);
|
||||
if ((methods & S5_AUTH_NONE) && can_auth_none(cx->sx, cx)) {
|
||||
s5_select_auth(parser, S5_AUTH_NONE);
|
||||
conn_write(incoming, "\5\0", 2); /* No auth required. */
|
||||
return s_req_start;
|
||||
}
|
||||
|
||||
if ((methods & S5_AUTH_PASSWD) && can_auth_passwd(cx->sx, cx)) {
|
||||
/* TODO(bnoordhuis) Implement username/password auth. */
|
||||
}
|
||||
|
||||
conn_write(incoming, "\5\377", 2); /* No acceptable auth. */
|
||||
return s_kill;
|
||||
}
|
||||
|
||||
/* TODO(bnoordhuis) Implement username/password auth. */
|
||||
static int do_handshake_auth(client_ctx *cx) {
|
||||
UNREACHABLE();
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
static int do_req_start(client_ctx *cx) {
|
||||
conn *incoming;
|
||||
|
||||
incoming = &cx->incoming;
|
||||
ASSERT(incoming->rdstate == c_stop);
|
||||
ASSERT(incoming->wrstate == c_done);
|
||||
incoming->wrstate = c_stop;
|
||||
|
||||
if (incoming->result < 0) {
|
||||
pr_err("write error: %s", uv_strerror(incoming->result));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
conn_read(incoming);
|
||||
return s_req_parse;
|
||||
}
|
||||
|
||||
static int do_req_parse(client_ctx *cx) {
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
s5_ctx *parser;
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
int err;
|
||||
|
||||
parser = &cx->parser;
|
||||
incoming = &cx->incoming;
|
||||
outgoing = &cx->outgoing;
|
||||
ASSERT(incoming->rdstate == c_done);
|
||||
ASSERT(incoming->wrstate == c_stop);
|
||||
ASSERT(outgoing->rdstate == c_stop);
|
||||
ASSERT(outgoing->wrstate == c_stop);
|
||||
incoming->rdstate = c_stop;
|
||||
|
||||
if (incoming->result < 0) {
|
||||
pr_err("read error: %s", uv_strerror(incoming->result));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
data = (uint8_t *) incoming->t.buf;
|
||||
size = (size_t) incoming->result;
|
||||
err = s5_parse(parser, &data, &size);
|
||||
if (err == s5_ok) {
|
||||
conn_read(incoming);
|
||||
return s_req_parse; /* Need more data. */
|
||||
}
|
||||
|
||||
if (size != 0) {
|
||||
pr_err("junk in request %u", (unsigned) size);
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
if (err != s5_exec_cmd) {
|
||||
pr_err("request error: %s", s5_strerror(err));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
if (parser->cmd == s5_cmd_tcp_bind) {
|
||||
/* Not supported but relatively straightforward to implement. */
|
||||
pr_warn("BIND requests are not supported.");
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
if (parser->cmd == s5_cmd_udp_assoc) {
|
||||
/* Not supported. Might be hard to implement because libuv has no
|
||||
* functionality for detecting the MTU size which the RFC mandates.
|
||||
*/
|
||||
pr_warn("UDP ASSOC requests are not supported.");
|
||||
return do_kill(cx);
|
||||
}
|
||||
ASSERT(parser->cmd == s5_cmd_tcp_connect);
|
||||
|
||||
if (parser->atyp == s5_atyp_host) {
|
||||
conn_getaddrinfo(outgoing, (const char *) parser->daddr);
|
||||
return s_req_lookup;
|
||||
}
|
||||
|
||||
if (parser->atyp == s5_atyp_ipv4) {
|
||||
memset(&outgoing->t.addr4, 0, sizeof(outgoing->t.addr4));
|
||||
outgoing->t.addr4.sin_family = AF_INET;
|
||||
outgoing->t.addr4.sin_port = htons(parser->dport);
|
||||
memcpy(&outgoing->t.addr4.sin_addr,
|
||||
parser->daddr,
|
||||
sizeof(outgoing->t.addr4.sin_addr));
|
||||
} else if (parser->atyp == s5_atyp_ipv6) {
|
||||
memset(&outgoing->t.addr6, 0, sizeof(outgoing->t.addr6));
|
||||
outgoing->t.addr6.sin6_family = AF_INET6;
|
||||
outgoing->t.addr6.sin6_port = htons(parser->dport);
|
||||
memcpy(&outgoing->t.addr6.sin6_addr,
|
||||
parser->daddr,
|
||||
sizeof(outgoing->t.addr6.sin6_addr));
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return do_req_connect_start(cx);
|
||||
}
|
||||
|
||||
static int do_req_lookup(client_ctx *cx) {
|
||||
s5_ctx *parser;
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
|
||||
parser = &cx->parser;
|
||||
incoming = &cx->incoming;
|
||||
outgoing = &cx->outgoing;
|
||||
ASSERT(incoming->rdstate == c_stop);
|
||||
ASSERT(incoming->wrstate == c_stop);
|
||||
ASSERT(outgoing->rdstate == c_stop);
|
||||
ASSERT(outgoing->wrstate == c_stop);
|
||||
|
||||
if (outgoing->result < 0) {
|
||||
/* TODO(bnoordhuis) Escape control characters in parser->daddr. */
|
||||
pr_err("lookup error for \"%s\": %s",
|
||||
parser->daddr,
|
||||
uv_strerror(outgoing->result));
|
||||
/* Send back a 'Host unreachable' reply. */
|
||||
conn_write(incoming, "\5\4\0\1\0\0\0\0\0\0", 10);
|
||||
return s_kill;
|
||||
}
|
||||
|
||||
/* Don't make assumptions about the offset of sin_port/sin6_port. */
|
||||
switch (outgoing->t.addr.sa_family) {
|
||||
case AF_INET:
|
||||
outgoing->t.addr4.sin_port = htons(parser->dport);
|
||||
break;
|
||||
case AF_INET6:
|
||||
outgoing->t.addr6.sin6_port = htons(parser->dport);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return do_req_connect_start(cx);
|
||||
}
|
||||
|
||||
/* Assumes that cx->outgoing.t.sa contains a valid AF_INET/AF_INET6 address. */
|
||||
static int do_req_connect_start(client_ctx *cx) {
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
int err;
|
||||
|
||||
incoming = &cx->incoming;
|
||||
outgoing = &cx->outgoing;
|
||||
ASSERT(incoming->rdstate == c_stop);
|
||||
ASSERT(incoming->wrstate == c_stop);
|
||||
ASSERT(outgoing->rdstate == c_stop);
|
||||
ASSERT(outgoing->wrstate == c_stop);
|
||||
|
||||
if (!can_access(cx->sx, cx, &outgoing->t.addr)) {
|
||||
pr_warn("connection not allowed by ruleset");
|
||||
/* Send a 'Connection not allowed by ruleset' reply. */
|
||||
conn_write(incoming, "\5\2\0\1\0\0\0\0\0\0", 10);
|
||||
return s_kill;
|
||||
}
|
||||
|
||||
err = conn_connect(outgoing);
|
||||
if (err != 0) {
|
||||
pr_err("connect error: %s\n", uv_strerror(err));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
return s_req_connect;
|
||||
}
|
||||
|
||||
static int do_req_connect(client_ctx *cx) {
|
||||
const struct sockaddr_in6 *in6;
|
||||
const struct sockaddr_in *in;
|
||||
char addr_storage[sizeof(*in6)];
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
uint8_t *buf;
|
||||
int addrlen;
|
||||
|
||||
incoming = &cx->incoming;
|
||||
outgoing = &cx->outgoing;
|
||||
ASSERT(incoming->rdstate == c_stop);
|
||||
ASSERT(incoming->wrstate == c_stop);
|
||||
ASSERT(outgoing->rdstate == c_stop);
|
||||
ASSERT(outgoing->wrstate == c_stop);
|
||||
|
||||
/* Build and send the reply. Not very pretty but gets the job done. */
|
||||
buf = (uint8_t *) incoming->t.buf;
|
||||
if (outgoing->result == 0) {
|
||||
/* The RFC mandates that the SOCKS server must include the local port
|
||||
* and address in the reply. So that's what we do.
|
||||
*/
|
||||
addrlen = sizeof(addr_storage);
|
||||
CHECK(0 == uv_tcp_getsockname(&outgoing->handle.tcp,
|
||||
(struct sockaddr *) addr_storage,
|
||||
&addrlen));
|
||||
buf[0] = 5; /* Version. */
|
||||
buf[1] = 0; /* Success. */
|
||||
buf[2] = 0; /* Reserved. */
|
||||
if (addrlen == sizeof(*in)) {
|
||||
buf[3] = 1; /* IPv4. */
|
||||
in = (const struct sockaddr_in *) &addr_storage;
|
||||
memcpy(buf + 4, &in->sin_addr, 4);
|
||||
memcpy(buf + 8, &in->sin_port, 2);
|
||||
conn_write(incoming, buf, 10);
|
||||
} else if (addrlen == sizeof(*in6)) {
|
||||
buf[3] = 4; /* IPv6. */
|
||||
in6 = (const struct sockaddr_in6 *) &addr_storage;
|
||||
memcpy(buf + 4, &in6->sin6_addr, 16);
|
||||
memcpy(buf + 20, &in6->sin6_port, 2);
|
||||
conn_write(incoming, buf, 22);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
return s_proxy_start;
|
||||
} else {
|
||||
pr_err("upstream connection error: %s\n", uv_strerror(outgoing->result));
|
||||
/* Send a 'Connection refused' reply. */
|
||||
conn_write(incoming, "\5\5\0\1\0\0\0\0\0\0", 10);
|
||||
return s_kill;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return s_kill;
|
||||
}
|
||||
|
||||
static int do_proxy_start(client_ctx *cx) {
|
||||
conn *incoming;
|
||||
conn *outgoing;
|
||||
|
||||
incoming = &cx->incoming;
|
||||
outgoing = &cx->outgoing;
|
||||
ASSERT(incoming->rdstate == c_stop);
|
||||
ASSERT(incoming->wrstate == c_done);
|
||||
ASSERT(outgoing->rdstate == c_stop);
|
||||
ASSERT(outgoing->wrstate == c_stop);
|
||||
incoming->wrstate = c_stop;
|
||||
|
||||
if (incoming->result < 0) {
|
||||
pr_err("write error: %s", uv_strerror(incoming->result));
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
conn_read(incoming);
|
||||
conn_read(outgoing);
|
||||
return s_proxy;
|
||||
}
|
||||
|
||||
/* Proxy incoming data back and forth. */
|
||||
static int do_proxy(client_ctx *cx) {
|
||||
if (conn_cycle("client", &cx->incoming, &cx->outgoing)) {
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
if (conn_cycle("upstream", &cx->outgoing, &cx->incoming)) {
|
||||
return do_kill(cx);
|
||||
}
|
||||
|
||||
return s_proxy;
|
||||
}
|
||||
|
||||
static int do_kill(client_ctx *cx) {
|
||||
int new_state;
|
||||
|
||||
if (cx->state >= s_almost_dead_0) {
|
||||
return cx->state;
|
||||
}
|
||||
|
||||
/* Try to cancel the request. The callback still runs but if the
|
||||
* cancellation succeeded, it gets called with status=UV_ECANCELED.
|
||||
*/
|
||||
new_state = s_almost_dead_1;
|
||||
if (cx->state == s_req_lookup) {
|
||||
new_state = s_almost_dead_0;
|
||||
uv_cancel(&cx->outgoing.t.req);
|
||||
}
|
||||
|
||||
conn_close(&cx->incoming);
|
||||
conn_close(&cx->outgoing);
|
||||
return new_state;
|
||||
}
|
||||
|
||||
static int do_almost_dead(client_ctx *cx) {
|
||||
ASSERT(cx->state >= s_almost_dead_0);
|
||||
return cx->state + 1; /* Another finalizer completed. */
|
||||
}
|
||||
|
||||
static int conn_cycle(const char *who, conn *a, conn *b) {
|
||||
if (a->result < 0) {
|
||||
if (a->result != UV_EOF) {
|
||||
pr_err("%s error: %s", who, uv_strerror(a->result));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b->result < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a->wrstate == c_done) {
|
||||
a->wrstate = c_stop;
|
||||
}
|
||||
|
||||
/* The logic is as follows: read when we don't write and write when we don't
|
||||
* read. That gives us back-pressure handling for free because if the peer
|
||||
* sends data faster than we consume it, TCP congestion control kicks in.
|
||||
*/
|
||||
if (a->wrstate == c_stop) {
|
||||
if (b->rdstate == c_stop) {
|
||||
conn_read(b);
|
||||
} else if (b->rdstate == c_done) {
|
||||
conn_write(a, b->t.buf, b->result);
|
||||
b->rdstate = c_stop; /* Triggers the call to conn_read() above. */
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void conn_timer_reset(conn *c) {
|
||||
CHECK(0 == uv_timer_start(&c->timer_handle,
|
||||
conn_timer_expire,
|
||||
c->idle_timeout,
|
||||
0));
|
||||
}
|
||||
|
||||
static void conn_timer_expire(uv_timer_t *handle, int status) {
|
||||
conn *c;
|
||||
|
||||
CHECK(0 == status);
|
||||
c = CONTAINER_OF(handle, conn, timer_handle);
|
||||
c->result = UV_ETIMEDOUT;
|
||||
do_next(c->client);
|
||||
}
|
||||
|
||||
static void conn_getaddrinfo(conn *c, const char *hostname) {
|
||||
struct addrinfo hints;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
CHECK(0 == uv_getaddrinfo(c->client->sx->loop,
|
||||
&c->t.addrinfo_req,
|
||||
conn_getaddrinfo_done,
|
||||
hostname,
|
||||
NULL,
|
||||
&hints));
|
||||
conn_timer_reset(c);
|
||||
}
|
||||
|
||||
static void conn_getaddrinfo_done(uv_getaddrinfo_t *req,
|
||||
int status,
|
||||
struct addrinfo *ai) {
|
||||
conn *c;
|
||||
|
||||
c = CONTAINER_OF(req, conn, t.addrinfo_req);
|
||||
c->result = status;
|
||||
|
||||
if (status == 0) {
|
||||
/* FIXME(bnoordhuis) Should try all addresses. */
|
||||
if (ai->ai_family == AF_INET) {
|
||||
c->t.addr4 = *(const struct sockaddr_in *) ai->ai_addr;
|
||||
} else if (ai->ai_family == AF_INET6) {
|
||||
c->t.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
uv_freeaddrinfo(ai);
|
||||
do_next(c->client);
|
||||
}
|
||||
|
||||
/* Assumes that c->t.sa contains a valid AF_INET or AF_INET6 address. */
|
||||
static int conn_connect(conn *c) {
|
||||
ASSERT(c->t.addr.sa_family == AF_INET ||
|
||||
c->t.addr.sa_family == AF_INET6);
|
||||
conn_timer_reset(c);
|
||||
return uv_tcp_connect(&c->t.connect_req,
|
||||
&c->handle.tcp,
|
||||
&c->t.addr,
|
||||
conn_connect_done);
|
||||
}
|
||||
|
||||
static void conn_connect_done(uv_connect_t *req, int status) {
|
||||
conn *c;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
return; /* Handle has been closed. */
|
||||
}
|
||||
|
||||
c = CONTAINER_OF(req, conn, t.connect_req);
|
||||
c->result = status;
|
||||
do_next(c->client);
|
||||
}
|
||||
|
||||
static void conn_read(conn *c) {
|
||||
ASSERT(c->rdstate == c_stop);
|
||||
CHECK(0 == uv_read_start(&c->handle.stream, conn_alloc, conn_read_done));
|
||||
c->rdstate = c_busy;
|
||||
conn_timer_reset(c);
|
||||
}
|
||||
|
||||
static void conn_read_done(uv_stream_t *handle,
|
||||
ssize_t nread,
|
||||
const uv_buf_t *buf) {
|
||||
conn *c;
|
||||
|
||||
c = CONTAINER_OF(handle, conn, handle);
|
||||
ASSERT(c->t.buf == buf->base);
|
||||
ASSERT(c->rdstate == c_busy);
|
||||
c->rdstate = c_done;
|
||||
c->result = nread;
|
||||
|
||||
uv_read_stop(&c->handle.stream);
|
||||
do_next(c->client);
|
||||
}
|
||||
|
||||
static void conn_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
|
||||
conn *c;
|
||||
|
||||
c = CONTAINER_OF(handle, conn, handle);
|
||||
ASSERT(c->rdstate == c_busy);
|
||||
buf->base = c->t.buf;
|
||||
buf->len = sizeof(c->t.buf);
|
||||
}
|
||||
|
||||
static void conn_write(conn *c, const void *data, unsigned int len) {
|
||||
uv_buf_t buf;
|
||||
|
||||
ASSERT(c->wrstate == c_stop || c->wrstate == c_done);
|
||||
c->wrstate = c_busy;
|
||||
|
||||
/* It's okay to cast away constness here, uv_write() won't modify the
|
||||
* memory.
|
||||
*/
|
||||
buf.base = (char *) data;
|
||||
buf.len = len;
|
||||
|
||||
CHECK(0 == uv_write(&c->write_req,
|
||||
&c->handle.stream,
|
||||
&buf,
|
||||
1,
|
||||
conn_write_done));
|
||||
conn_timer_reset(c);
|
||||
}
|
||||
|
||||
static void conn_write_done(uv_write_t *req, int status) {
|
||||
conn *c;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
return; /* Handle has been closed. */
|
||||
}
|
||||
|
||||
c = CONTAINER_OF(req, conn, write_req);
|
||||
ASSERT(c->wrstate == c_busy);
|
||||
c->wrstate = c_done;
|
||||
c->result = status;
|
||||
do_next(c->client);
|
||||
}
|
||||
|
||||
static void conn_close(conn *c) {
|
||||
ASSERT(c->rdstate != c_dead);
|
||||
ASSERT(c->wrstate != c_dead);
|
||||
c->rdstate = c_dead;
|
||||
c->wrstate = c_dead;
|
||||
c->timer_handle.data = c;
|
||||
c->handle.handle.data = c;
|
||||
uv_close(&c->handle.handle, conn_close_done);
|
||||
uv_close((uv_handle_t *) &c->timer_handle, conn_close_done);
|
||||
}
|
||||
|
||||
static void conn_close_done(uv_handle_t *handle) {
|
||||
conn *c;
|
||||
|
||||
c = handle->data;
|
||||
do_next(c->client);
|
||||
}
|
||||
Vendored
+139
@@ -0,0 +1,139 @@
|
||||
/* Copyright StrongLoop, Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef DEFS_H_
|
||||
#define DEFS_H_
|
||||
|
||||
#include "s5.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <netinet/in.h> /* sockaddr_in, sockaddr_in6 */
|
||||
#include <stddef.h> /* size_t, ssize_t */
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h> /* sockaddr */
|
||||
|
||||
struct client_ctx;
|
||||
|
||||
typedef struct {
|
||||
const char *bind_host;
|
||||
unsigned short bind_port;
|
||||
unsigned int idle_timeout;
|
||||
} server_config;
|
||||
|
||||
typedef struct {
|
||||
unsigned int idle_timeout; /* Connection idle timeout in ms. */
|
||||
uv_tcp_t tcp_handle;
|
||||
uv_loop_t *loop;
|
||||
} server_ctx;
|
||||
|
||||
typedef struct {
|
||||
unsigned char rdstate;
|
||||
unsigned char wrstate;
|
||||
unsigned int idle_timeout;
|
||||
struct client_ctx *client; /* Backlink to owning client context. */
|
||||
ssize_t result;
|
||||
union {
|
||||
uv_handle_t handle;
|
||||
uv_stream_t stream;
|
||||
uv_tcp_t tcp;
|
||||
uv_udp_t udp;
|
||||
} handle;
|
||||
uv_timer_t timer_handle; /* For detecting timeouts. */
|
||||
uv_write_t write_req;
|
||||
/* We only need one of these at a time so make them share memory. */
|
||||
union {
|
||||
uv_getaddrinfo_t addrinfo_req;
|
||||
uv_connect_t connect_req;
|
||||
uv_req_t req;
|
||||
struct sockaddr_in6 addr6;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr addr;
|
||||
char buf[2048]; /* Scratch space. Used to read data into. */
|
||||
} t;
|
||||
} conn;
|
||||
|
||||
typedef struct client_ctx {
|
||||
unsigned int state;
|
||||
server_ctx *sx; /* Backlink to owning server context. */
|
||||
s5_ctx parser; /* The SOCKS protocol parser. */
|
||||
conn incoming; /* Connection with the SOCKS client. */
|
||||
conn outgoing; /* Connection with upstream. */
|
||||
} client_ctx;
|
||||
|
||||
/* server.c */
|
||||
int server_run(const server_config *cf, uv_loop_t *loop);
|
||||
int can_auth_none(const server_ctx *sx, const client_ctx *cx);
|
||||
int can_auth_passwd(const server_ctx *sx, const client_ctx *cx);
|
||||
int can_access(const server_ctx *sx,
|
||||
const client_ctx *cx,
|
||||
const struct sockaddr *addr);
|
||||
|
||||
/* client.c */
|
||||
void client_finish_init(server_ctx *sx, client_ctx *cx);
|
||||
|
||||
/* util.c */
|
||||
#if defined(__GNUC__)
|
||||
# define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__((format(printf, a, b)))
|
||||
#else
|
||||
# define ATTRIBUTE_FORMAT_PRINTF(a, b)
|
||||
#endif
|
||||
void pr_info(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
void pr_warn(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
void pr_err(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2);
|
||||
void *xmalloc(size_t size);
|
||||
|
||||
/* main.c */
|
||||
const char *_getprogname(void);
|
||||
|
||||
/* getopt.c */
|
||||
#if !HAVE_UNISTD_H
|
||||
extern char *optarg;
|
||||
int getopt(int argc, char **argv, const char *options);
|
||||
#endif
|
||||
|
||||
/* ASSERT() is for debug checks, CHECK() for run-time sanity checks.
|
||||
* DEBUG_CHECKS is for expensive debug checks that we only want to
|
||||
* enable in debug builds but still want type-checked by the compiler
|
||||
* in release builds.
|
||||
*/
|
||||
#if defined(NDEBUG)
|
||||
# define ASSERT(exp)
|
||||
# define CHECK(exp) do { if (!(exp)) abort(); } while (0)
|
||||
# define DEBUG_CHECKS (0)
|
||||
#else
|
||||
# define ASSERT(exp) assert(exp)
|
||||
# define CHECK(exp) assert(exp)
|
||||
# define DEBUG_CHECKS (1)
|
||||
#endif
|
||||
|
||||
#define UNREACHABLE() CHECK(!"Unreachable code reached.")
|
||||
|
||||
/* This macro looks complicated but it's not: it calculates the address
|
||||
* of the embedding struct through the address of the embedded struct.
|
||||
* In other words, if struct A embeds struct B, then we can obtain
|
||||
* the address of A by taking the address of B and subtracting the
|
||||
* field offset of B in A.
|
||||
*/
|
||||
#define CONTAINER_OF(ptr, type, field) \
|
||||
((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
|
||||
|
||||
#endif /* DEFS_H_ */
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/* $NetBSD: getopt.c,v 1.26 2003/08/07 16:43:40 agc Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1987, 1993, 1994
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if defined(LIBC_SCCS) && !defined(lint)
|
||||
static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95";
|
||||
#endif /* LIBC_SCCS and not lint */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern const char *_getprogname(void);
|
||||
|
||||
int opterr = 1, /* if error message should be printed */
|
||||
optind = 1, /* index into parent argv vector */
|
||||
optopt, /* character checked for validity */
|
||||
optreset; /* reset getopt */
|
||||
char *optarg; /* argument associated with option */
|
||||
|
||||
#define BADCH (int)'?'
|
||||
#define BADARG (int)':'
|
||||
#define EMSG ""
|
||||
|
||||
/*
|
||||
* getopt --
|
||||
* Parse argc/argv argument vector.
|
||||
*/
|
||||
int
|
||||
getopt(nargc, nargv, ostr)
|
||||
int nargc;
|
||||
char * const nargv[];
|
||||
const char *ostr;
|
||||
{
|
||||
static char *place = EMSG; /* option letter processing */
|
||||
char *oli; /* option letter list index */
|
||||
|
||||
if (optreset || *place == 0) { /* update scanning pointer */
|
||||
optreset = 0;
|
||||
place = nargv[optind];
|
||||
if (optind >= nargc || *place++ != '-') {
|
||||
/* Argument is absent or is not an option */
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
optopt = *place++;
|
||||
if (optopt == '-' && *place == 0) {
|
||||
/* "--" => end of options */
|
||||
++optind;
|
||||
place = EMSG;
|
||||
return (-1);
|
||||
}
|
||||
if (optopt == 0) {
|
||||
/* Solitary '-', treat as a '-' option
|
||||
if the program (eg su) is looking for it. */
|
||||
place = EMSG;
|
||||
if (strchr(ostr, '-') == NULL)
|
||||
return (-1);
|
||||
optopt = '-';
|
||||
}
|
||||
} else
|
||||
optopt = *place++;
|
||||
|
||||
/* See if option letter is one the caller wanted... */
|
||||
if (optopt == ':' || (oli = strchr(ostr, optopt)) == NULL) {
|
||||
if (*place == 0)
|
||||
++optind;
|
||||
if (opterr && *ostr != ':')
|
||||
(void)fprintf(stderr,
|
||||
"%s: illegal option -- %c\n", _getprogname(),
|
||||
optopt);
|
||||
return (BADCH);
|
||||
}
|
||||
|
||||
/* Does this option need an argument? */
|
||||
if (oli[1] != ':') {
|
||||
/* don't need argument */
|
||||
optarg = NULL;
|
||||
if (*place == 0)
|
||||
++optind;
|
||||
} else {
|
||||
/* Option-argument is either the rest of this argument or the
|
||||
entire next argument. */
|
||||
if (*place)
|
||||
optarg = place;
|
||||
else if (nargc > ++optind)
|
||||
optarg = nargv[optind];
|
||||
else {
|
||||
/* option-argument absent */
|
||||
place = EMSG;
|
||||
if (*ostr == ':')
|
||||
return (BADARG);
|
||||
if (opterr)
|
||||
(void)fprintf(stderr,
|
||||
"%s: option requires an argument -- %c\n",
|
||||
_getprogname(), optopt);
|
||||
return (BADCH);
|
||||
}
|
||||
place = EMSG;
|
||||
++optind;
|
||||
}
|
||||
return (optopt); /* return option letter */
|
||||
}
|
||||
Vendored
+99
@@ -0,0 +1,99 @@
|
||||
/* Copyright StrongLoop, Inc. 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 "defs.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_UNISTD_H
|
||||
#include <unistd.h> /* getopt */
|
||||
#endif
|
||||
|
||||
#define DEFAULT_BIND_HOST "127.0.0.1"
|
||||
#define DEFAULT_BIND_PORT 1080
|
||||
#define DEFAULT_IDLE_TIMEOUT (60 * 1000)
|
||||
|
||||
static void parse_opts(server_config *cf, int argc, char **argv);
|
||||
static void usage(void);
|
||||
|
||||
static const char *progname = __FILE__; /* Reset in main(). */
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
server_config config;
|
||||
int err;
|
||||
|
||||
progname = argv[0];
|
||||
memset(&config, 0, sizeof(config));
|
||||
config.bind_host = DEFAULT_BIND_HOST;
|
||||
config.bind_port = DEFAULT_BIND_PORT;
|
||||
config.idle_timeout = DEFAULT_IDLE_TIMEOUT;
|
||||
parse_opts(&config, argc, argv);
|
||||
|
||||
err = server_run(&config, uv_default_loop());
|
||||
if (err) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *_getprogname(void) {
|
||||
return progname;
|
||||
}
|
||||
|
||||
static void parse_opts(server_config *cf, int argc, char **argv) {
|
||||
int opt;
|
||||
|
||||
while (-1 != (opt = getopt(argc, argv, "H:hp:"))) {
|
||||
switch (opt) {
|
||||
case 'H':
|
||||
cf->bind_host = optarg;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (1 != sscanf(optarg, "%hu", &cf->bind_port)) {
|
||||
pr_err("bad port number: %s", optarg);
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage:\n"
|
||||
"\n"
|
||||
" %s [-b <address> [-h] [-p <port>]\n"
|
||||
"\n"
|
||||
"Options:\n"
|
||||
"\n"
|
||||
" -b <hostname|address> Bind to this address or hostname.\n"
|
||||
" Default: \"127.0.0.1\"\n"
|
||||
" -h Show this help message.\n"
|
||||
" -p <port> Bind to this port number. Default: 1080\n"
|
||||
"",
|
||||
progname);
|
||||
exit(1);
|
||||
}
|
||||
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
/* Copyright StrongLoop, Inc. 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 "s5.h"
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> /* abort() */
|
||||
#include <string.h> /* memset() */
|
||||
|
||||
enum {
|
||||
s5_version,
|
||||
s5_nmethods,
|
||||
s5_methods,
|
||||
s5_auth_pw_version,
|
||||
s5_auth_pw_userlen,
|
||||
s5_auth_pw_username,
|
||||
s5_auth_pw_passlen,
|
||||
s5_auth_pw_password,
|
||||
s5_req_version,
|
||||
s5_req_cmd,
|
||||
s5_req_reserved,
|
||||
s5_req_atyp,
|
||||
s5_req_atyp_host,
|
||||
s5_req_daddr,
|
||||
s5_req_dport0,
|
||||
s5_req_dport1,
|
||||
s5_dead
|
||||
};
|
||||
|
||||
void s5_init(s5_ctx *cx) {
|
||||
memset(cx, 0, sizeof(*cx));
|
||||
cx->state = s5_version;
|
||||
}
|
||||
|
||||
s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size) {
|
||||
s5_err err;
|
||||
uint8_t *p;
|
||||
uint8_t c;
|
||||
size_t i;
|
||||
size_t n;
|
||||
|
||||
p = *data;
|
||||
n = *size;
|
||||
i = 0;
|
||||
|
||||
while (i < n) {
|
||||
c = p[i];
|
||||
i += 1;
|
||||
switch (cx->state) {
|
||||
case s5_version:
|
||||
if (c != 5) {
|
||||
err = s5_bad_version;
|
||||
goto out;
|
||||
}
|
||||
cx->state = s5_nmethods;
|
||||
break;
|
||||
|
||||
case s5_nmethods:
|
||||
cx->arg0 = 0;
|
||||
cx->arg1 = c; /* Number of bytes to read. */
|
||||
cx->state = s5_methods;
|
||||
break;
|
||||
|
||||
case s5_methods:
|
||||
if (cx->arg0 < cx->arg1) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
cx->methods |= S5_AUTH_NONE;
|
||||
break;
|
||||
case 1:
|
||||
cx->methods |= S5_AUTH_GSSAPI;
|
||||
break;
|
||||
case 2:
|
||||
cx->methods |= S5_AUTH_PASSWD;
|
||||
break;
|
||||
/* Ignore everything we don't understand. */
|
||||
}
|
||||
cx->arg0 += 1;
|
||||
}
|
||||
if (cx->arg0 == cx->arg1) {
|
||||
err = s5_auth_select;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case s5_auth_pw_version:
|
||||
if (c != 1) {
|
||||
err = s5_bad_version;
|
||||
goto out;
|
||||
}
|
||||
cx->state = s5_auth_pw_userlen;
|
||||
break;
|
||||
|
||||
case s5_auth_pw_userlen:
|
||||
cx->arg0 = 0;
|
||||
cx->userlen = c;
|
||||
cx->state = s5_auth_pw_username;
|
||||
break;
|
||||
|
||||
case s5_auth_pw_username:
|
||||
if (cx->arg0 < cx->userlen) {
|
||||
cx->username[cx->arg0] = c;
|
||||
cx->arg0 += 1;
|
||||
}
|
||||
if (cx->arg0 == cx->userlen) {
|
||||
cx->username[cx->userlen] = '\0';
|
||||
cx->state = s5_auth_pw_passlen;
|
||||
}
|
||||
break;
|
||||
|
||||
case s5_auth_pw_passlen:
|
||||
cx->arg0 = 0;
|
||||
cx->passlen = c;
|
||||
cx->state = s5_auth_pw_password;
|
||||
break;
|
||||
|
||||
case s5_auth_pw_password:
|
||||
if (cx->arg0 < cx->passlen) {
|
||||
cx->password[cx->arg0] = c;
|
||||
cx->arg0 += 1;
|
||||
}
|
||||
if (cx->arg0 == cx->passlen) {
|
||||
cx->password[cx->passlen] = '\0';
|
||||
cx->state = s5_req_version;
|
||||
err = s5_auth_verify;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case s5_req_version:
|
||||
if (c != 5) {
|
||||
err = s5_bad_version;
|
||||
goto out;
|
||||
}
|
||||
cx->state = s5_req_cmd;
|
||||
break;
|
||||
|
||||
case s5_req_cmd:
|
||||
switch (c) {
|
||||
case 1: /* TCP connect */
|
||||
cx->cmd = s5_cmd_tcp_connect;
|
||||
break;
|
||||
case 3: /* UDP associate */
|
||||
cx->cmd = s5_cmd_udp_assoc;
|
||||
break;
|
||||
default:
|
||||
err = s5_bad_cmd;
|
||||
goto out;
|
||||
}
|
||||
cx->state = s5_req_reserved;
|
||||
break;
|
||||
|
||||
case s5_req_reserved:
|
||||
cx->state = s5_req_atyp;
|
||||
break;
|
||||
|
||||
case s5_req_atyp:
|
||||
cx->arg0 = 0;
|
||||
switch (c) {
|
||||
case 1: /* IPv4, four octets. */
|
||||
cx->state = s5_req_daddr;
|
||||
cx->atyp = s5_atyp_ipv4;
|
||||
cx->arg1 = 4;
|
||||
break;
|
||||
case 3: /* Hostname. First byte is length. */
|
||||
cx->state = s5_req_atyp_host;
|
||||
cx->atyp = s5_atyp_host;
|
||||
cx->arg1 = 0;
|
||||
break;
|
||||
case 4: /* IPv6, sixteen octets. */
|
||||
cx->state = s5_req_daddr;
|
||||
cx->atyp = s5_atyp_ipv6;
|
||||
cx->arg1 = 16;
|
||||
break;
|
||||
default:
|
||||
err = s5_bad_atyp;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case s5_req_atyp_host:
|
||||
cx->arg1 = c;
|
||||
cx->state = s5_req_daddr;
|
||||
break;
|
||||
|
||||
case s5_req_daddr:
|
||||
if (cx->arg0 < cx->arg1) {
|
||||
cx->daddr[cx->arg0] = c;
|
||||
cx->arg0 += 1;
|
||||
}
|
||||
if (cx->arg0 == cx->arg1) {
|
||||
cx->daddr[cx->arg1] = '\0';
|
||||
cx->state = s5_req_dport0;
|
||||
}
|
||||
break;
|
||||
|
||||
case s5_req_dport0:
|
||||
cx->dport = c << 8;
|
||||
cx->state = s5_req_dport1;
|
||||
break;
|
||||
|
||||
case s5_req_dport1:
|
||||
cx->dport |= c;
|
||||
cx->state = s5_dead;
|
||||
err = s5_exec_cmd;
|
||||
goto out;
|
||||
|
||||
case s5_dead:
|
||||
break;
|
||||
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
err = s5_ok;
|
||||
|
||||
out:
|
||||
*data = p + i;
|
||||
*size = n - i;
|
||||
return err;
|
||||
}
|
||||
|
||||
unsigned int s5_auth_methods(const s5_ctx *cx) {
|
||||
return cx->methods;
|
||||
}
|
||||
|
||||
int s5_select_auth(s5_ctx *cx, s5_auth_method method) {
|
||||
int err;
|
||||
|
||||
err = 0;
|
||||
switch (method) {
|
||||
case S5_AUTH_NONE:
|
||||
cx->state = s5_req_version;
|
||||
break;
|
||||
case S5_AUTH_PASSWD:
|
||||
cx->state = s5_auth_pw_version;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
const char *s5_strerror(s5_err err) {
|
||||
#define S5_ERR_GEN(_, name, errmsg) case s5_ ## name: return errmsg;
|
||||
switch (err) {
|
||||
S5_ERR_MAP(S5_ERR_GEN)
|
||||
default: ; /* Silence s5_max_errors -Wswitch warning. */
|
||||
}
|
||||
#undef S5_ERR_GEN
|
||||
return "Unknown error.";
|
||||
}
|
||||
Vendored
+94
@@ -0,0 +1,94 @@
|
||||
/* Copyright StrongLoop, Inc. 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.
|
||||
*/
|
||||
|
||||
#ifndef S5_H_
|
||||
#define S5_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define S5_ERR_MAP(V) \
|
||||
V(-1, bad_version, "Bad protocol version.") \
|
||||
V(-2, bad_cmd, "Bad protocol command.") \
|
||||
V(-3, bad_atyp, "Bad address type.") \
|
||||
V(0, ok, "No error.") \
|
||||
V(1, auth_select, "Select authentication method.") \
|
||||
V(2, auth_verify, "Verify authentication.") \
|
||||
V(3, exec_cmd, "Execute command.") \
|
||||
|
||||
typedef enum {
|
||||
#define S5_ERR_GEN(code, name, _) s5_ ## name = code,
|
||||
S5_ERR_MAP(S5_ERR_GEN)
|
||||
#undef S5_ERR_GEN
|
||||
s5_max_errors
|
||||
} s5_err;
|
||||
|
||||
typedef enum {
|
||||
S5_AUTH_NONE = 1 << 0,
|
||||
S5_AUTH_GSSAPI = 1 << 1,
|
||||
S5_AUTH_PASSWD = 1 << 2
|
||||
} s5_auth_method;
|
||||
|
||||
typedef enum {
|
||||
s5_auth_allow,
|
||||
s5_auth_deny
|
||||
} s5_auth_result;
|
||||
|
||||
typedef enum {
|
||||
s5_atyp_ipv4,
|
||||
s5_atyp_ipv6,
|
||||
s5_atyp_host
|
||||
} s5_atyp;
|
||||
|
||||
typedef enum {
|
||||
s5_cmd_tcp_connect,
|
||||
s5_cmd_tcp_bind,
|
||||
s5_cmd_udp_assoc
|
||||
} s5_cmd;
|
||||
|
||||
typedef struct {
|
||||
uint32_t arg0; /* Scratch space for the state machine. */
|
||||
uint32_t arg1; /* Scratch space for the state machine. */
|
||||
uint8_t state;
|
||||
uint8_t methods;
|
||||
uint8_t cmd;
|
||||
uint8_t atyp;
|
||||
uint8_t userlen;
|
||||
uint8_t passlen;
|
||||
uint16_t dport;
|
||||
uint8_t username[257];
|
||||
uint8_t password[257];
|
||||
uint8_t daddr[257]; /* TODO(bnoordhuis) Merge with username/password. */
|
||||
} s5_ctx;
|
||||
|
||||
void s5_init(s5_ctx *ctx);
|
||||
|
||||
s5_err s5_parse(s5_ctx *cx, uint8_t **data, size_t *size);
|
||||
|
||||
/* Only call after s5_parse() has returned s5_want_auth_method. */
|
||||
unsigned int s5_auth_methods(const s5_ctx *cx);
|
||||
|
||||
/* Call after s5_parse() has returned s5_want_auth_method. */
|
||||
int s5_select_auth(s5_ctx *cx, s5_auth_method method);
|
||||
|
||||
const char *s5_strerror(s5_err err);
|
||||
|
||||
#endif /* S5_H_ */
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/* Copyright StrongLoop, Inc. 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 "defs.h"
|
||||
#include <netinet/in.h> /* INET6_ADDRSTRLEN */
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef INET6_ADDRSTRLEN
|
||||
# define INET6_ADDRSTRLEN 63
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uv_getaddrinfo_t getaddrinfo_req;
|
||||
server_config config;
|
||||
server_ctx *servers;
|
||||
uv_loop_t *loop;
|
||||
} server_state;
|
||||
|
||||
static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *ai);
|
||||
static void on_connection(uv_stream_t *server, int status);
|
||||
|
||||
int server_run(const server_config *cf, uv_loop_t *loop) {
|
||||
struct addrinfo hints;
|
||||
server_state state;
|
||||
int err;
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
state.servers = NULL;
|
||||
state.config = *cf;
|
||||
state.loop = loop;
|
||||
|
||||
/* Resolve the address of the interface that we should bind to.
|
||||
* The getaddrinfo callback starts the server and everything else.
|
||||
*/
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
err = uv_getaddrinfo(loop,
|
||||
&state.getaddrinfo_req,
|
||||
do_bind,
|
||||
cf->bind_host,
|
||||
NULL,
|
||||
&hints);
|
||||
if (err != 0) {
|
||||
pr_err("getaddrinfo: %s", uv_strerror(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Start the event loop. Control continues in do_bind(). */
|
||||
if (uv_run(loop, UV_RUN_DEFAULT)) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Please Valgrind. */
|
||||
uv_loop_delete(loop);
|
||||
free(state.servers);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Bind a server to each address that getaddrinfo() reported. */
|
||||
static void do_bind(uv_getaddrinfo_t *req, int status, struct addrinfo *addrs) {
|
||||
char addrbuf[INET6_ADDRSTRLEN + 1];
|
||||
unsigned int ipv4_naddrs;
|
||||
unsigned int ipv6_naddrs;
|
||||
server_state *state;
|
||||
server_config *cf;
|
||||
struct addrinfo *ai;
|
||||
const void *addrv;
|
||||
const char *what;
|
||||
uv_loop_t *loop;
|
||||
server_ctx *sx;
|
||||
unsigned int n;
|
||||
int err;
|
||||
union {
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
} s;
|
||||
|
||||
state = CONTAINER_OF(req, server_state, getaddrinfo_req);
|
||||
loop = state->loop;
|
||||
cf = &state->config;
|
||||
|
||||
if (status < 0) {
|
||||
pr_err("getaddrinfo(\"%s\"): %s", cf->bind_host, uv_strerror(status));
|
||||
uv_freeaddrinfo(addrs);
|
||||
return;
|
||||
}
|
||||
|
||||
ipv4_naddrs = 0;
|
||||
ipv6_naddrs = 0;
|
||||
for (ai = addrs; ai != NULL; ai = ai->ai_next) {
|
||||
if (ai->ai_family == AF_INET) {
|
||||
ipv4_naddrs += 1;
|
||||
} else if (ai->ai_family == AF_INET6) {
|
||||
ipv6_naddrs += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv4_naddrs == 0 && ipv6_naddrs == 0) {
|
||||
pr_err("%s has no IPv4/6 addresses", cf->bind_host);
|
||||
uv_freeaddrinfo(addrs);
|
||||
return;
|
||||
}
|
||||
|
||||
state->servers =
|
||||
xmalloc((ipv4_naddrs + ipv6_naddrs) * sizeof(state->servers[0]));
|
||||
|
||||
n = 0;
|
||||
for (ai = addrs; ai != NULL; ai = ai->ai_next) {
|
||||
if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ai->ai_family == AF_INET) {
|
||||
s.addr4 = *(const struct sockaddr_in *) ai->ai_addr;
|
||||
s.addr4.sin_port = htons(cf->bind_port);
|
||||
addrv = &s.addr4.sin_addr;
|
||||
} else if (ai->ai_family == AF_INET6) {
|
||||
s.addr6 = *(const struct sockaddr_in6 *) ai->ai_addr;
|
||||
s.addr6.sin6_port = htons(cf->bind_port);
|
||||
addrv = &s.addr6.sin6_addr;
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
if (uv_inet_ntop(s.addr.sa_family, addrv, addrbuf, sizeof(addrbuf))) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
sx = state->servers + n;
|
||||
sx->loop = loop;
|
||||
sx->idle_timeout = state->config.idle_timeout;
|
||||
CHECK(0 == uv_tcp_init(loop, &sx->tcp_handle));
|
||||
|
||||
what = "uv_tcp_bind";
|
||||
err = uv_tcp_bind(&sx->tcp_handle, &s.addr);
|
||||
if (err == 0) {
|
||||
what = "uv_listen";
|
||||
err = uv_listen((uv_stream_t *) &sx->tcp_handle, 128, on_connection);
|
||||
}
|
||||
|
||||
if (err != 0) {
|
||||
pr_err("%s(\"%s:%hu\"): %s",
|
||||
what,
|
||||
addrbuf,
|
||||
cf->bind_port,
|
||||
uv_strerror(err));
|
||||
while (n > 0) {
|
||||
n -= 1;
|
||||
uv_close((uv_handle_t *) (state->servers + n), NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
pr_info("listening on %s:%hu", addrbuf, cf->bind_port);
|
||||
n += 1;
|
||||
}
|
||||
|
||||
uv_freeaddrinfo(addrs);
|
||||
}
|
||||
|
||||
static void on_connection(uv_stream_t *server, int status) {
|
||||
server_ctx *sx;
|
||||
client_ctx *cx;
|
||||
|
||||
CHECK(status == 0);
|
||||
sx = CONTAINER_OF(server, server_ctx, tcp_handle);
|
||||
cx = xmalloc(sizeof(*cx));
|
||||
CHECK(0 == uv_tcp_init(sx->loop, &cx->incoming.handle.tcp));
|
||||
CHECK(0 == uv_accept(server, &cx->incoming.handle.stream));
|
||||
client_finish_init(sx, cx);
|
||||
}
|
||||
|
||||
int can_auth_none(const server_ctx *sx, const client_ctx *cx) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int can_auth_passwd(const server_ctx *sx, const client_ctx *cx) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int can_access(const server_ctx *sx,
|
||||
const client_ctx *cx,
|
||||
const struct sockaddr *addr) {
|
||||
const struct sockaddr_in6 *addr6;
|
||||
const struct sockaddr_in *addr4;
|
||||
const uint32_t *p;
|
||||
uint32_t a;
|
||||
uint32_t b;
|
||||
uint32_t c;
|
||||
uint32_t d;
|
||||
|
||||
/* TODO(bnoordhuis) Implement proper access checks. For now, just reject
|
||||
* traffic to localhost.
|
||||
*/
|
||||
if (addr->sa_family == AF_INET) {
|
||||
addr4 = (const struct sockaddr_in *) addr;
|
||||
d = ntohl(addr4->sin_addr.s_addr);
|
||||
return (d >> 24) != 0x7F;
|
||||
}
|
||||
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
addr6 = (const struct sockaddr_in6 *) addr;
|
||||
p = (const uint32_t *) &addr6->sin6_addr.s6_addr;
|
||||
a = ntohl(p[0]);
|
||||
b = ntohl(p[1]);
|
||||
c = ntohl(p[2]);
|
||||
d = ntohl(p[3]);
|
||||
if (a == 0 && b == 0 && c == 0 && d == 1) {
|
||||
return 0; /* "::1" style address. */
|
||||
}
|
||||
if (a == 0 && b == 0 && c == 0xFFFF && (d >> 24) == 0x7F) {
|
||||
return 0; /* "::ffff:127.x.x.x" style address. */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+72
@@ -0,0 +1,72 @@
|
||||
/* Copyright StrongLoop, Inc. 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 "defs.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static void pr_do(FILE *stream,
|
||||
const char *label,
|
||||
const char *fmt,
|
||||
va_list ap);
|
||||
|
||||
void *xmalloc(size_t size) {
|
||||
void *ptr;
|
||||
|
||||
ptr = malloc(size);
|
||||
if (ptr == NULL) {
|
||||
pr_err("out of memory, need %lu bytes", (unsigned long) size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void pr_info(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
pr_do(stdout, "info", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void pr_warn(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
pr_do(stderr, "warn", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void pr_err(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
pr_do(stderr, "error", fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void pr_do(FILE *stream,
|
||||
const char *label,
|
||||
const char *fmt,
|
||||
va_list ap) {
|
||||
char fmtbuf[1024];
|
||||
vsnprintf(fmtbuf, sizeof(fmtbuf), fmt, ap);
|
||||
fprintf(stream, "%s:%s: %s\n", _getprogname(), label, fmtbuf);
|
||||
}
|
||||
Vendored
+6
-6
@@ -19,20 +19,20 @@
|
||||
typedef void *QUEUE[2];
|
||||
|
||||
/* Private macros. */
|
||||
#define QUEUE_NEXT(q) ((*(q))[0])
|
||||
#define QUEUE_PREV(q) ((*(q))[1])
|
||||
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT((QUEUE *) QUEUE_PREV(q)))
|
||||
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV((QUEUE *) QUEUE_NEXT(q)))
|
||||
#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0]))
|
||||
#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1]))
|
||||
#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q)))
|
||||
#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q)))
|
||||
|
||||
/* Public macros. */
|
||||
#define QUEUE_DATA(ptr, type, field) \
|
||||
((type *) ((char *) (ptr) - ((char *) &((type *) 0)->field)))
|
||||
|
||||
#define QUEUE_FOREACH(q, h) \
|
||||
for ((q) = (QUEUE *) (*(h))[0]; (q) != (h); (q) = (QUEUE *) (*(q))[0])
|
||||
for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q))
|
||||
|
||||
#define QUEUE_EMPTY(q) \
|
||||
(QUEUE_NEXT(q) == (q))
|
||||
((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q))
|
||||
|
||||
#define QUEUE_HEAD(q) \
|
||||
(QUEUE_NEXT(q))
|
||||
|
||||
Vendored
+1
-1
@@ -45,7 +45,7 @@
|
||||
#include <sys/proc.h>
|
||||
#include <sys/procfs.h>
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
uint64_t G = 1000000000;
|
||||
timebasestruct_t t;
|
||||
read_wall_time(&t, TIMEBASE_SZ);
|
||||
|
||||
Vendored
+58
-4
@@ -73,7 +73,7 @@ STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len));
|
||||
|
||||
|
||||
uint64_t uv_hrtime(void) {
|
||||
return uv__hrtime();
|
||||
return uv__hrtime(UV_CLOCK_PRECISE);
|
||||
}
|
||||
|
||||
|
||||
@@ -542,6 +542,44 @@ int uv__dup(int fd) {
|
||||
}
|
||||
|
||||
|
||||
ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
|
||||
struct cmsghdr* cmsg;
|
||||
ssize_t rc;
|
||||
int* pfd;
|
||||
int* end;
|
||||
#if defined(__linux__)
|
||||
static int no_msg_cmsg_cloexec;
|
||||
if (no_msg_cmsg_cloexec == 0) {
|
||||
rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */
|
||||
if (rc != -1)
|
||||
return rc;
|
||||
if (errno != EINVAL)
|
||||
return -errno;
|
||||
rc = recvmsg(fd, msg, flags);
|
||||
if (rc == -1)
|
||||
return -errno;
|
||||
no_msg_cmsg_cloexec = 1;
|
||||
} else {
|
||||
rc = recvmsg(fd, msg, flags);
|
||||
}
|
||||
#else
|
||||
rc = recvmsg(fd, msg, flags);
|
||||
#endif
|
||||
if (rc == -1)
|
||||
return -errno;
|
||||
if (msg->msg_controllen == 0)
|
||||
return rc;
|
||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg))
|
||||
if (cmsg->cmsg_type == SCM_RIGHTS)
|
||||
for (pfd = (int*) CMSG_DATA(cmsg),
|
||||
end = (int*) ((char*) cmsg + cmsg->cmsg_len);
|
||||
pfd < end;
|
||||
pfd += 1)
|
||||
uv__cloexec(*pfd, 1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int uv_cwd(char* buffer, size_t size) {
|
||||
if (buffer == NULL)
|
||||
return -EINVAL;
|
||||
@@ -604,20 +642,33 @@ static unsigned int next_power_of_two(unsigned int val) {
|
||||
|
||||
static void maybe_resize(uv_loop_t* loop, unsigned int len) {
|
||||
uv__io_t** watchers;
|
||||
void* fake_watcher_list;
|
||||
void* fake_watcher_count;
|
||||
unsigned int nwatchers;
|
||||
unsigned int i;
|
||||
|
||||
if (len <= loop->nwatchers)
|
||||
return;
|
||||
|
||||
nwatchers = next_power_of_two(len);
|
||||
watchers = realloc(loop->watchers, nwatchers * sizeof(loop->watchers[0]));
|
||||
/* Preserve fake watcher list and count at the end of the watchers */
|
||||
if (loop->watchers != NULL) {
|
||||
fake_watcher_list = loop->watchers[loop->nwatchers];
|
||||
fake_watcher_count = loop->watchers[loop->nwatchers + 1];
|
||||
} else {
|
||||
fake_watcher_list = NULL;
|
||||
fake_watcher_count = NULL;
|
||||
}
|
||||
|
||||
nwatchers = next_power_of_two(len + 2) - 2;
|
||||
watchers = realloc(loop->watchers,
|
||||
(nwatchers + 2) * sizeof(loop->watchers[0]));
|
||||
|
||||
if (watchers == NULL)
|
||||
abort();
|
||||
|
||||
for (i = loop->nwatchers; i < nwatchers; i++)
|
||||
watchers[i] = NULL;
|
||||
watchers[nwatchers] = fake_watcher_list;
|
||||
watchers[nwatchers + 1] = fake_watcher_count;
|
||||
|
||||
loop->watchers = watchers;
|
||||
loop->nwatchers = nwatchers;
|
||||
@@ -709,6 +760,9 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
|
||||
uv__io_stop(loop, w, UV__POLLIN | UV__POLLOUT);
|
||||
QUEUE_REMOVE(&w->pending_queue);
|
||||
|
||||
/* Remove stale events for this file descriptor */
|
||||
uv__platform_invalidate_fd(loop, w->fd);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -52,7 +52,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
mach_timebase_info_data_t info;
|
||||
|
||||
if (mach_timebase_info(&info) != KERN_SUCCESS)
|
||||
|
||||
Vendored
+1
-1
@@ -67,7 +67,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
|
||||
|
||||
Vendored
+2
@@ -170,6 +170,8 @@ skip:
|
||||
#elif defined(__APPLE__) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__sun)
|
||||
struct timeval tv[2];
|
||||
tv[0].tv_sec = req->atime;
|
||||
|
||||
Vendored
+138
-63
@@ -73,6 +73,17 @@ typedef struct uv__fsevents_event_s uv__fsevents_event_t;
|
||||
typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
|
||||
typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
|
||||
|
||||
struct uv__cf_loop_signal_s {
|
||||
QUEUE member;
|
||||
uv_fs_event_t* handle;
|
||||
};
|
||||
|
||||
struct uv__fsevents_event_s {
|
||||
QUEUE member;
|
||||
int events;
|
||||
char path[1];
|
||||
};
|
||||
|
||||
struct uv__cf_loop_state_s {
|
||||
CFRunLoopRef loop;
|
||||
CFRunLoopSourceRef signal_source;
|
||||
@@ -81,18 +92,7 @@ struct uv__cf_loop_state_s {
|
||||
uv_sem_t fsevent_sem;
|
||||
uv_mutex_t fsevent_mutex;
|
||||
void* fsevent_handles[2];
|
||||
int fsevent_handle_count;
|
||||
};
|
||||
|
||||
struct uv__cf_loop_signal_s {
|
||||
QUEUE member;
|
||||
uv_fs_event_t* handle;
|
||||
};
|
||||
|
||||
struct uv__fsevents_event_s {
|
||||
int events;
|
||||
void* next;
|
||||
char path[1];
|
||||
unsigned int fsevent_handle_count;
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
@@ -120,9 +120,9 @@ static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
|
||||
static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
|
||||
static void (*pCFRunLoopStop)(CFRunLoopRef);
|
||||
static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
|
||||
static CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||
const char*,
|
||||
CFStringEncoding);
|
||||
static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
|
||||
CFAllocatorRef,
|
||||
const char*);
|
||||
static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
|
||||
static CFStringRef (*pkCFRunLoopDefaultMode);
|
||||
static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
|
||||
@@ -143,22 +143,36 @@ static void (*pFSEventStreamStop)(FSEventStreamRef);
|
||||
|
||||
#define UV__FSEVENTS_PROCESS(handle, block) \
|
||||
do { \
|
||||
QUEUE events; \
|
||||
QUEUE* q; \
|
||||
uv__fsevents_event_t* event; \
|
||||
uv__fsevents_event_t* next; \
|
||||
int err; \
|
||||
uv_mutex_lock(&(handle)->cf_mutex); \
|
||||
event = (handle)->cf_event; \
|
||||
(handle)->cf_event = NULL; \
|
||||
/* Split-off all events and empty original queue */ \
|
||||
QUEUE_INIT(&events); \
|
||||
if (!QUEUE_EMPTY(&(handle)->cf_events)) { \
|
||||
q = QUEUE_HEAD(&(handle)->cf_events); \
|
||||
QUEUE_SPLIT(&(handle)->cf_events, q, &events); \
|
||||
} \
|
||||
/* Get error (if any) and zero original one */ \
|
||||
err = (handle)->cf_error; \
|
||||
(handle)->cf_error = 0; \
|
||||
uv_mutex_unlock(&(handle)->cf_mutex); \
|
||||
while (event != NULL) { \
|
||||
/* Invoke callback */ \
|
||||
/* Invoke block code, but only if handle wasn't closed */ \
|
||||
if (!uv__is_closing((handle))) \
|
||||
/* Loop through events, deallocating each after processing */ \
|
||||
while (!QUEUE_EMPTY(&events)) { \
|
||||
q = QUEUE_HEAD(&events); \
|
||||
event = QUEUE_DATA(q, uv__fsevents_event_t, member); \
|
||||
QUEUE_REMOVE(q); \
|
||||
/* NOTE: Checking uv__is_active() is required here, because handle \
|
||||
* callback may close handle and invoking it after it will lead to \
|
||||
* incorrect behaviour */ \
|
||||
if (!uv__is_closing((handle)) && uv__is_active((handle))) \
|
||||
block \
|
||||
/* Free allocated data */ \
|
||||
next = event->next; \
|
||||
free(event); \
|
||||
event = next; \
|
||||
} \
|
||||
if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
|
||||
(handle)->cb((handle), NULL, 0, err); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@@ -169,12 +183,28 @@ static void uv__fsevents_cb(uv_async_t* cb, int status) {
|
||||
handle = cb->data;
|
||||
|
||||
UV__FSEVENTS_PROCESS(handle, {
|
||||
if (handle->event_watcher.fd != -1)
|
||||
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
|
||||
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
|
||||
});
|
||||
}
|
||||
|
||||
if (!uv__is_closing(handle) && handle->event_watcher.fd == -1)
|
||||
uv__fsevents_close(handle);
|
||||
|
||||
/* Runs in CF thread, pushed event into handle's event list */
|
||||
static void uv__fsevents_push_event(uv_fs_event_t* handle,
|
||||
QUEUE* events,
|
||||
int err) {
|
||||
assert(events != NULL || err != 0);
|
||||
uv_mutex_lock(&handle->cf_mutex);
|
||||
|
||||
/* Concatenate two queues */
|
||||
if (events != NULL)
|
||||
QUEUE_ADD(&handle->cf_events, events);
|
||||
|
||||
/* Propagate error */
|
||||
if (err != 0)
|
||||
handle->cf_error = err;
|
||||
uv_mutex_unlock(&handle->cf_mutex);
|
||||
|
||||
uv_async_send(handle->cf_cb);
|
||||
}
|
||||
|
||||
|
||||
@@ -195,7 +225,7 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
|
||||
uv_loop_t* loop;
|
||||
uv__cf_loop_state_t* state;
|
||||
uv__fsevents_event_t* event;
|
||||
uv__fsevents_event_t* tail;
|
||||
QUEUE head;
|
||||
|
||||
loop = info;
|
||||
state = loop->cf_state;
|
||||
@@ -203,9 +233,10 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
|
||||
paths = eventPaths;
|
||||
|
||||
/* For each handle */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
||||
handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
tail = NULL;
|
||||
QUEUE_INIT(&head);
|
||||
|
||||
/* Process and filter out events */
|
||||
for (i = 0; i < numEvents; i++) {
|
||||
@@ -260,25 +291,18 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
|
||||
else
|
||||
event->events = UV_RENAME;
|
||||
|
||||
if (tail != NULL)
|
||||
tail->next = event;
|
||||
tail = event;
|
||||
QUEUE_INSERT_TAIL(&head, &event->member);
|
||||
}
|
||||
|
||||
if (tail != NULL) {
|
||||
uv_mutex_lock(&handle->cf_mutex);
|
||||
tail->next = handle->cf_event;
|
||||
handle->cf_event = tail;
|
||||
uv_mutex_unlock(&handle->cf_mutex);
|
||||
|
||||
uv_async_send(handle->cf_cb);
|
||||
}
|
||||
if (!QUEUE_EMPTY(&head))
|
||||
uv__fsevents_push_event(handle, &head, 0);
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
}
|
||||
|
||||
|
||||
/* Runs in CF thread */
|
||||
static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
||||
static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
||||
uv__cf_loop_state_t* state;
|
||||
FSEventStreamContext ctx;
|
||||
FSEventStreamRef ref;
|
||||
@@ -292,10 +316,21 @@ static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
||||
ctx.release = NULL;
|
||||
ctx.copyDescription = NULL;
|
||||
|
||||
latency = 0.15;
|
||||
latency = 0.05;
|
||||
|
||||
/* Set appropriate flags */
|
||||
flags = kFSEventStreamCreateFlagFileEvents;
|
||||
/* Explanation of selected flags:
|
||||
* 1. NoDefer - without this flag, events that are happening continuously
|
||||
* (i.e. each event is happening after time interval less than `latency`,
|
||||
* counted from previous event), will be deferred and passed to callback
|
||||
* once they'll either fill whole OS buffer, or when this continuous stream
|
||||
* will stop (i.e. there'll be delay between events, bigger than
|
||||
* `latency`).
|
||||
* Specifying this flag will invoke callback after `latency` time passed
|
||||
* since event.
|
||||
* 2. FileEvents - fire callback for file changes too (by default it is firing
|
||||
* it only for directory changes).
|
||||
*/
|
||||
flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
|
||||
|
||||
/*
|
||||
* NOTE: It might sound like a good idea to remember last seen StreamEventId,
|
||||
@@ -316,10 +351,14 @@ static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
||||
pFSEventStreamScheduleWithRunLoop(ref,
|
||||
state->loop,
|
||||
*pkCFRunLoopDefaultMode);
|
||||
if (!pFSEventStreamStart(ref))
|
||||
abort();
|
||||
if (!pFSEventStreamStart(ref)) {
|
||||
pFSEventStreamInvalidate(ref);
|
||||
pFSEventStreamRelease(ref);
|
||||
return -EMFILE;
|
||||
}
|
||||
|
||||
state->fsevent_stream = ref;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -352,10 +391,16 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
|
||||
uv_fs_event_t* curr;
|
||||
CFArrayRef cf_paths;
|
||||
CFStringRef* paths;
|
||||
int i;
|
||||
int path_count;
|
||||
unsigned int i;
|
||||
int err;
|
||||
unsigned int path_count;
|
||||
|
||||
state = handle->loop->cf_state;
|
||||
paths = NULL;
|
||||
cf_paths = NULL;
|
||||
err = 0;
|
||||
/* NOTE: `i` is used in deallocation loop below */
|
||||
i = 0;
|
||||
|
||||
/* Optimization to prevent O(n^2) time spent when starting to watch
|
||||
* many files simultaneously
|
||||
@@ -371,39 +416,68 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
|
||||
/* Destroy previous FSEventStream */
|
||||
uv__fsevents_destroy_stream(handle->loop);
|
||||
|
||||
/* Any failure below will be a memory failure */
|
||||
err = -ENOMEM;
|
||||
|
||||
/* Create list of all watched paths */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
path_count = state->fsevent_handle_count;
|
||||
if (path_count != 0) {
|
||||
paths = malloc(sizeof(*paths) * path_count);
|
||||
if (paths == NULL)
|
||||
abort();
|
||||
if (paths == NULL) {
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
goto final;
|
||||
}
|
||||
|
||||
q = &state->fsevent_handles;
|
||||
for (i = 0; i < path_count; i++) {
|
||||
for (; i < path_count; i++) {
|
||||
q = QUEUE_NEXT(q);
|
||||
assert(q != &state->fsevent_handles);
|
||||
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
|
||||
assert(curr->realpath != NULL);
|
||||
paths[i] = pCFStringCreateWithCString(NULL,
|
||||
curr->realpath,
|
||||
pCFStringGetSystemEncoding());
|
||||
if (paths[i] == NULL)
|
||||
abort();
|
||||
paths[i] =
|
||||
pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
|
||||
if (paths[i] == NULL) {
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
goto final;
|
||||
}
|
||||
}
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
err = 0;
|
||||
|
||||
if (path_count != 0) {
|
||||
/* Create new FSEventStream */
|
||||
cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
|
||||
if (cf_paths == NULL)
|
||||
abort();
|
||||
uv__fsevents_create_stream(handle->loop, cf_paths);
|
||||
if (cf_paths == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto final;
|
||||
}
|
||||
err = uv__fsevents_create_stream(handle->loop, cf_paths);
|
||||
}
|
||||
|
||||
final:
|
||||
/* Deallocate all paths in case of failure */
|
||||
if (err != 0) {
|
||||
if (cf_paths == NULL) {
|
||||
while (i != 0)
|
||||
pCFRelease(paths[--i]);
|
||||
free(paths);
|
||||
} else {
|
||||
/* CFArray takes ownership of both strings and original C-array */
|
||||
pCFRelease(cf_paths);
|
||||
}
|
||||
|
||||
/* Broadcast error to all handles */
|
||||
uv_mutex_lock(&state->fsevent_mutex);
|
||||
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
||||
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
||||
uv__fsevents_push_event(curr, NULL, err);
|
||||
}
|
||||
uv_mutex_unlock(&state->fsevent_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main thread will block until the removal of handle from the list,
|
||||
* we must tell it when we're ready.
|
||||
@@ -464,7 +538,7 @@ static int uv__fsevents_global_init(void) {
|
||||
V(core_foundation_handle, CFRunLoopSourceSignal);
|
||||
V(core_foundation_handle, CFRunLoopStop);
|
||||
V(core_foundation_handle, CFRunLoopWakeUp);
|
||||
V(core_foundation_handle, CFStringCreateWithCString);
|
||||
V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
|
||||
V(core_foundation_handle, CFStringGetSystemEncoding);
|
||||
V(core_foundation_handle, kCFRunLoopDefaultMode);
|
||||
V(core_services_handle, FSEventStreamCreate);
|
||||
@@ -722,8 +796,9 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||
return -errno;
|
||||
handle->realpath_len = strlen(handle->realpath);
|
||||
|
||||
/* Initialize singly-linked list */
|
||||
handle->cf_event = NULL;
|
||||
/* Initialize event queue */
|
||||
QUEUE_INIT(&handle->cf_events);
|
||||
handle->cf_error = 0;
|
||||
|
||||
/*
|
||||
* Events will occur in other thread.
|
||||
|
||||
Vendored
+12
-2
@@ -128,12 +128,18 @@ enum {
|
||||
UV_TCP_SINGLE_ACCEPT = 0x1000 /* Only accept() when idle. */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */
|
||||
UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */
|
||||
} uv_clocktype_t;
|
||||
|
||||
/* core */
|
||||
int uv__nonblock(int fd, int set);
|
||||
int uv__close(int fd);
|
||||
int uv__cloexec(int fd, int set);
|
||||
int uv__socket(int domain, int type, int protocol);
|
||||
int uv__dup(int fd);
|
||||
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
|
||||
void uv__make_close_pending(uv_handle_t* handle);
|
||||
|
||||
void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd);
|
||||
@@ -191,10 +197,11 @@ void uv__work_submit(uv_loop_t* loop,
|
||||
void uv__work_done(uv_async_t* handle, int status);
|
||||
|
||||
/* platform specific */
|
||||
uint64_t uv__hrtime(void);
|
||||
uint64_t uv__hrtime(uv_clocktype_t type);
|
||||
int uv__kqueue_init(uv_loop_t* loop);
|
||||
int uv__platform_loop_init(uv_loop_t* loop, int default_loop);
|
||||
void uv__platform_loop_delete(uv_loop_t* loop);
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd);
|
||||
|
||||
/* various */
|
||||
void uv__async_close(uv_async_t* handle);
|
||||
@@ -236,6 +243,7 @@ void uv__fsevents_loop_delete(uv_loop_t* loop);
|
||||
/* OSX < 10.7 has no file events, polyfill them */
|
||||
#ifndef MAC_OS_X_VERSION_10_7
|
||||
|
||||
static const int kFSEventStreamCreateFlagNoDefer = 0x00000002;
|
||||
static const int kFSEventStreamCreateFlagFileEvents = 0x00000010;
|
||||
static const int kFSEventStreamEventFlagItemCreated = 0x00000100;
|
||||
static const int kFSEventStreamEventFlagItemRemoved = 0x00000200;
|
||||
@@ -263,7 +271,9 @@ UV_UNUSED(static void uv__req_init(uv_loop_t* loop,
|
||||
uv__req_init((loop), (uv_req_t*)(req), (type))
|
||||
|
||||
UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) {
|
||||
loop->time = uv__hrtime() / 1000000;
|
||||
/* Use a fast time source if available. We only need millisecond precision.
|
||||
*/
|
||||
loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000;
|
||||
}
|
||||
|
||||
UV_UNUSED(static char* uv__basename_r(const char* path)) {
|
||||
|
||||
Vendored
+30
-2
@@ -161,11 +161,18 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
ev = events + i;
|
||||
fd = ev->ident;
|
||||
w = loop->watchers[fd];
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
if (w == NULL) {
|
||||
/* File descriptor that we've stopped watching, disarm it. */
|
||||
/* TODO batch up */
|
||||
@@ -190,7 +197,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
revents = 0;
|
||||
|
||||
if (ev->filter == EVFILT_READ) {
|
||||
if (w->events & UV__POLLIN) {
|
||||
if (w->pevents & UV__POLLIN) {
|
||||
revents |= UV__POLLIN;
|
||||
w->rcount = ev->data;
|
||||
} else {
|
||||
@@ -204,7 +211,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
}
|
||||
|
||||
if (ev->filter == EVFILT_WRITE) {
|
||||
if (w->events & UV__POLLOUT) {
|
||||
if (w->pevents & UV__POLLOUT) {
|
||||
revents |= UV__POLLOUT;
|
||||
w->wcount = ev->data;
|
||||
} else {
|
||||
@@ -226,6 +233,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
w->cb(loop, w, revents);
|
||||
nevents++;
|
||||
}
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
@@ -254,6 +263,25 @@ update_timeout:
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent* events;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct kevent*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events == NULL)
|
||||
return;
|
||||
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].ident == fd)
|
||||
events[i].ident = -1;
|
||||
}
|
||||
|
||||
|
||||
static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
|
||||
uv_fs_event_t* handle;
|
||||
struct kevent ev;
|
||||
|
||||
Vendored
+91
-8
@@ -52,8 +52,10 @@
|
||||
# include <linux/if_packet.h>
|
||||
#endif
|
||||
|
||||
#undef NANOSEC
|
||||
#define NANOSEC ((uint64_t) 1e9)
|
||||
/* Available from 2.6.32 onwards. */
|
||||
#ifndef CLOCK_MONOTONIC_COARSE
|
||||
# define CLOCK_MONOTONIC_COARSE 6
|
||||
#endif
|
||||
|
||||
/* This is rather annoying: CLOCK_BOOTTIME lives in <linux/time.h> but we can't
|
||||
* include that file because it conflicts with <time.h>. We'll just have to
|
||||
@@ -103,6 +105,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct uv__epoll_event* events;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct uv__epoll_event*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events == NULL)
|
||||
return;
|
||||
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].data == fd)
|
||||
events[i].data = -1;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct uv__epoll_event events[1024];
|
||||
struct uv__epoll_event* pe;
|
||||
@@ -195,10 +216,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
pe = events + i;
|
||||
fd = pe->data;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
@@ -214,9 +242,38 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
w->cb(loop, w, pe->events);
|
||||
nevents++;
|
||||
/* Give users only events they're interested in. Prevents spurious
|
||||
* callbacks when previous callback invocation in this loop has stopped
|
||||
* the current watcher. Also, filters out events that users has not
|
||||
* requested us to watch.
|
||||
*/
|
||||
pe->events &= w->pevents | UV__POLLERR | UV__POLLHUP;
|
||||
|
||||
/* Work around an epoll quirk where it sometimes reports just the
|
||||
* EPOLLERR or EPOLLHUP event. In order to force the event loop to
|
||||
* move forward, we merge in the read/write events that the watcher
|
||||
* is interested in; uv__read() and uv__write() will then deal with
|
||||
* the error or hangup in the usual fashion.
|
||||
*
|
||||
* Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user
|
||||
* reads the available data, calls uv_read_stop(), then sometime later
|
||||
* calls uv_read_start() again. By then, libuv has forgotten about the
|
||||
* hangup and the kernel won't report EPOLLIN again because there's
|
||||
* nothing left to read. If anything, libuv is to blame here. The
|
||||
* current hack is just a quick bandaid; to properly fix it, libuv
|
||||
* needs to remember the error/hangup event. We should get that for
|
||||
* free when we switch over to edge-triggered I/O.
|
||||
*/
|
||||
if (pe->events == UV__EPOLLERR || pe->events == UV__EPOLLHUP)
|
||||
pe->events |= w->pevents & (UV__EPOLLIN | UV__EPOLLOUT);
|
||||
|
||||
if (pe->events != 0) {
|
||||
w->cb(loop, w, pe->events);
|
||||
nevents++;
|
||||
}
|
||||
}
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
@@ -245,10 +302,36 @@ update_timeout:
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
static clock_t fast_clock_id = -1;
|
||||
struct timespec t;
|
||||
clock_t clock_id;
|
||||
|
||||
/* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has
|
||||
* millisecond granularity or better. CLOCK_MONOTONIC_COARSE is
|
||||
* serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may
|
||||
* decide to make a costly system call.
|
||||
*/
|
||||
/* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE
|
||||
* when it has microsecond granularity or better (unlikely).
|
||||
*/
|
||||
if (type == UV_CLOCK_FAST && fast_clock_id == -1) {
|
||||
if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 &&
|
||||
t.tv_nsec <= 1 * 1000 * 1000) {
|
||||
fast_clock_id = CLOCK_MONOTONIC_COARSE;
|
||||
} else {
|
||||
fast_clock_id = CLOCK_MONOTONIC;
|
||||
}
|
||||
}
|
||||
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
if (type == UV_CLOCK_FAST)
|
||||
clock_id = fast_clock_id;
|
||||
|
||||
if (clock_gettime(clock_id, &t))
|
||||
return 0; /* Not really possible. */
|
||||
|
||||
return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -96,7 +96,7 @@ static int uv__loop_init(uv_loop_t* loop, int default_loop) {
|
||||
QUEUE_INIT(&loop->watcher_queue);
|
||||
|
||||
loop->closing_handles = NULL;
|
||||
loop->time = uv__hrtime() / 1000000;
|
||||
uv__update_time(loop);
|
||||
uv__async_init(&loop->async_watcher);
|
||||
loop->signal_pipefd[0] = -1;
|
||||
loop->signal_pipefd[1] = -1;
|
||||
|
||||
Vendored
+1
-1
@@ -57,7 +57,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
|
||||
|
||||
Vendored
+1
-1
@@ -56,7 +56,7 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
|
||||
|
||||
Vendored
+4
-2
@@ -72,7 +72,8 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
|
||||
sockfd = err;
|
||||
|
||||
memset(&saddr, 0, sizeof saddr);
|
||||
uv_strlcpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path));
|
||||
strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1);
|
||||
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
|
||||
@@ -167,7 +168,8 @@ void uv_pipe_connect(uv_connect_t* req,
|
||||
}
|
||||
|
||||
memset(&saddr, 0, sizeof saddr);
|
||||
uv_strlcpy(saddr.sun_path, name, sizeof(saddr.sun_path));
|
||||
strncpy(saddr.sun_path, name, sizeof(saddr.sun_path) - 1);
|
||||
saddr.sun_path[sizeof(saddr.sun_path) - 1] = '\0';
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
do {
|
||||
|
||||
Vendored
+11
-10
@@ -109,9 +109,6 @@ static void uv__chld(uv_signal_t* handle, int signum) {
|
||||
if (WIFSIGNALED(process->status))
|
||||
term_signal = WTERMSIG(process->status);
|
||||
|
||||
if (process->errorno != 0)
|
||||
exit_status = process->errorno; /* execve() failed */
|
||||
|
||||
process->exit_cb(process, exit_status, term_signal);
|
||||
}
|
||||
}
|
||||
@@ -359,6 +356,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
ssize_t r;
|
||||
pid_t pid;
|
||||
int err;
|
||||
int exec_errorno;
|
||||
int i;
|
||||
|
||||
assert(options->file != NULL);
|
||||
@@ -434,14 +432,14 @@ int uv_spawn(uv_loop_t* loop,
|
||||
uv__close(signal_pipe[1]);
|
||||
|
||||
process->status = 0;
|
||||
process->errorno = 0;
|
||||
exec_errorno = 0;
|
||||
do
|
||||
r = read(signal_pipe[0], &process->errorno, sizeof(process->errorno));
|
||||
r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno));
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == 0)
|
||||
; /* okay, EOF */
|
||||
else if (r == sizeof(process->errorno))
|
||||
else if (r == sizeof(exec_errorno))
|
||||
; /* okay, read errorno */
|
||||
else if (r == -1 && errno == EPIPE)
|
||||
; /* okay, got EPIPE */
|
||||
@@ -461,15 +459,18 @@ int uv_spawn(uv_loop_t* loop,
|
||||
goto error;
|
||||
}
|
||||
|
||||
q = uv__process_queue(loop, pid);
|
||||
QUEUE_INSERT_TAIL(q, &process->queue);
|
||||
/* Only activate this handle if exec() happened successfully */
|
||||
if (exec_errorno == 0) {
|
||||
q = uv__process_queue(loop, pid);
|
||||
QUEUE_INSERT_TAIL(q, &process->queue);
|
||||
uv__handle_start(process);
|
||||
}
|
||||
|
||||
process->pid = pid;
|
||||
process->exit_cb = options->exit_cb;
|
||||
uv__handle_start(process);
|
||||
|
||||
free(pipes);
|
||||
return 0;
|
||||
return exec_errorno;
|
||||
|
||||
error:
|
||||
if (pipes != NULL) {
|
||||
|
||||
Vendored
+2
-2
@@ -298,7 +298,7 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) {
|
||||
|
||||
kq = kqueue();
|
||||
if (kq == -1) {
|
||||
fprintf(stderr, "(libuv) Failed to create kqueue (%d)\n", errno);
|
||||
perror("(libuv) kqueue()");
|
||||
return -errno;
|
||||
}
|
||||
|
||||
@@ -1007,7 +1007,7 @@ static void uv__read(uv_stream_t* stream) {
|
||||
msg.msg_control = (void*) cmsg_space;
|
||||
|
||||
do {
|
||||
nread = recvmsg(uv__stream_fd(stream), &msg, 0);
|
||||
nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0);
|
||||
}
|
||||
while (nread < 0 && errno == EINTR);
|
||||
}
|
||||
|
||||
Vendored
+29
-1
@@ -97,6 +97,25 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
struct port_event* events;
|
||||
uintptr_t i;
|
||||
uintptr_t nfds;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
|
||||
events = (struct port_event*) loop->watchers[loop->nwatchers];
|
||||
nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
|
||||
if (events == NULL)
|
||||
return;
|
||||
|
||||
/* Invalidate events with same file descriptor */
|
||||
for (i = 0; i < nfds; i++)
|
||||
if ((int) events[i].portev_object == fd)
|
||||
events[i].portev_object = -1;
|
||||
}
|
||||
|
||||
|
||||
void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
struct port_event events[1024];
|
||||
struct port_event* pe;
|
||||
@@ -183,10 +202,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
|
||||
nevents = 0;
|
||||
|
||||
assert(loop->watchers != NULL);
|
||||
loop->watchers[loop->nwatchers] = (void*) events;
|
||||
loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds;
|
||||
for (i = 0; i < nfds; i++) {
|
||||
pe = events + i;
|
||||
fd = pe->portev_object;
|
||||
|
||||
/* Skip invalidated events, see uv__platform_invalidate_fd */
|
||||
if (fd == -1)
|
||||
continue;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert((unsigned) fd < loop->nwatchers);
|
||||
|
||||
@@ -206,6 +232,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue))
|
||||
QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue);
|
||||
}
|
||||
loop->watchers[loop->nwatchers] = NULL;
|
||||
loop->watchers[loop->nwatchers + 1] = NULL;
|
||||
|
||||
if (nevents != 0) {
|
||||
if (nfds == ARRAY_SIZE(events) && --count != 0) {
|
||||
@@ -239,7 +267,7 @@ update_timeout:
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(void) {
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
return gethrtime();
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -335,7 +335,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
|
||||
ts.tv_nsec = timeout % NANOSEC;
|
||||
r = pthread_cond_timedwait_relative_np(cond, mutex, &ts);
|
||||
#else
|
||||
timeout += uv__hrtime();
|
||||
timeout += uv__hrtime(UV_CLOCK_PRECISE);
|
||||
ts.tv_sec = timeout / NANOSEC;
|
||||
ts.tv_nsec = timeout % NANOSEC;
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
Vendored
-34
@@ -66,40 +66,6 @@ size_t uv_req_size(uv_req_type type) {
|
||||
|
||||
#undef XX
|
||||
|
||||
size_t uv_strlcpy(char* dst, const char* src, size_t size) {
|
||||
size_t n;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < (size - 1) && *src != '\0'; n++)
|
||||
*dst++ = *src++;
|
||||
|
||||
*dst = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
size_t uv_strlcat(char* dst, const char* src, size_t size) {
|
||||
size_t n;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
for (n = 0; n < size && *dst != '\0'; n++, dst++);
|
||||
|
||||
if (n == size)
|
||||
return n;
|
||||
|
||||
while (n < (size - 1) && *src != '\0')
|
||||
n++, *dst++ = *src++;
|
||||
|
||||
*dst = '\0';
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
uv_buf_t uv_buf_init(char* base, unsigned int len) {
|
||||
uv_buf_t buf;
|
||||
|
||||
Vendored
+1
-1
@@ -31,7 +31,7 @@
|
||||
|
||||
#define UV_VERSION_MAJOR 0
|
||||
#define UV_VERSION_MINOR 11
|
||||
#define UV_VERSION_PATCH 14
|
||||
#define UV_VERSION_PATCH 15
|
||||
#define UV_VERSION_IS_RELEASE 1
|
||||
|
||||
|
||||
|
||||
Vendored
+1
-5
@@ -738,11 +738,7 @@ void fs__readdir(uv_fs_t* req) {
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "malloc");
|
||||
}
|
||||
|
||||
#ifdef _CRT_NON_CONFORMING_SWPRINTFS
|
||||
swprintf(path2, fmt, pathw);
|
||||
#else
|
||||
swprintf(path2, len + 3, fmt, pathw);
|
||||
#endif
|
||||
_snwprintf(path2, len + 3, fmt, pathw);
|
||||
dir = FindFirstFileW(path2, &ent);
|
||||
free(path2);
|
||||
|
||||
|
||||
Vendored
+7
-16
@@ -127,7 +127,6 @@ static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) {
|
||||
uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS);
|
||||
handle->exit_cb = NULL;
|
||||
handle->pid = 0;
|
||||
handle->spawn_error = 0;
|
||||
handle->exit_signal = 0;
|
||||
handle->wait_handle = INVALID_HANDLE_VALUE;
|
||||
handle->process_handle = INVALID_HANDLE_VALUE;
|
||||
@@ -752,10 +751,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
|
||||
/* callback.*/
|
||||
uv__handle_stop(handle);
|
||||
|
||||
if (handle->spawn_error) {
|
||||
/* Spawning failed. */
|
||||
exit_code = uv_translate_sys_error(handle->spawn_error);
|
||||
} else if (GetExitCodeProcess(handle->process_handle, &status)) {
|
||||
if (GetExitCodeProcess(handle->process_handle, &status)) {
|
||||
exit_code = status;
|
||||
} else {
|
||||
/* Unable to to obtain the exit code. This should never happen. */
|
||||
@@ -1025,25 +1021,20 @@ int uv_spawn(uv_loop_t* loop,
|
||||
free(env);
|
||||
free(path);
|
||||
|
||||
process->spawn_error = err;
|
||||
|
||||
if (process->child_stdio_buffer != NULL) {
|
||||
/* Clean up child stdio handles. */
|
||||
uv__stdio_destroy(process->child_stdio_buffer);
|
||||
process->child_stdio_buffer = NULL;
|
||||
}
|
||||
|
||||
/* Make the handle active. It will remain active until the exit callback */
|
||||
/* is made or the handle is closed, whichever happens first. */
|
||||
uv__handle_start(process);
|
||||
|
||||
/* If an error happened, queue the exit req. */
|
||||
if (err) {
|
||||
process->exit_cb_pending = 1;
|
||||
uv_insert_pending_req(loop, (uv_req_t*) &process->exit_req);
|
||||
/* Make the handle active, but only if an error didn't happen. It will */
|
||||
/* remain active until the exit callback is made or the handle is closed, */
|
||||
/* whichever happens first. */
|
||||
if (err == 0) {
|
||||
uv__handle_start(process);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
|
||||
/* This code path is taken when we run into an error that we want to */
|
||||
/* report immediately. */
|
||||
|
||||
Vendored
+23
@@ -32,6 +32,11 @@
|
||||
# include <stdint.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# include <sys/time.h>
|
||||
# include <sys/resource.h> /* setrlimit() */
|
||||
#endif
|
||||
|
||||
#define TEST_PORT 9123
|
||||
#define TEST_PORT_2 9124
|
||||
|
||||
@@ -153,6 +158,24 @@ enum test_status {
|
||||
return TEST_SKIP; \
|
||||
} while (0)
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
# define TEST_FILE_LIMIT(num) \
|
||||
do { \
|
||||
struct rlimit lim; \
|
||||
lim.rlim_cur = (num); \
|
||||
lim.rlim_max = lim.rlim_cur; \
|
||||
if (setrlimit(RLIMIT_NOFILE, &lim)) \
|
||||
RETURN_SKIP("File descriptor limit too low."); \
|
||||
} while (0)
|
||||
|
||||
#else /* defined(_WIN32) */
|
||||
|
||||
# define TEST_FILE_LIMIT(num) do {} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined _WIN32 && ! defined __GNUC__
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
Vendored
+77
@@ -0,0 +1,77 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
#include "uv.h"
|
||||
#include "task.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static unsigned int read_cb_called;
|
||||
|
||||
static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) {
|
||||
static char slab[1];
|
||||
buf->base = slab;
|
||||
buf->len = sizeof(slab);
|
||||
}
|
||||
|
||||
static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) {
|
||||
switch (++read_cb_called) {
|
||||
case 1:
|
||||
ASSERT(nread == 1);
|
||||
uv_read_stop(handle);
|
||||
break;
|
||||
case 2:
|
||||
ASSERT(nread == UV_EOF);
|
||||
uv_close((uv_handle_t *) handle, NULL);
|
||||
break;
|
||||
default:
|
||||
ASSERT(!"read_cb_called > 2");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_IMPL(close_fd) {
|
||||
uv_pipe_t pipe_handle;
|
||||
int fd[2];
|
||||
|
||||
ASSERT(0 == pipe(fd));
|
||||
ASSERT(0 == fcntl(fd[0], F_SETFL, O_NONBLOCK));
|
||||
ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0));
|
||||
ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0]));
|
||||
fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */
|
||||
ASSERT(1 == write(fd[1], "", 1));
|
||||
ASSERT(0 == close(fd[1]));
|
||||
fd[1] = -1;
|
||||
ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb));
|
||||
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
|
||||
ASSERT(1 == read_cb_called);
|
||||
ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle));
|
||||
ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb));
|
||||
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
|
||||
ASSERT(2 == read_cb_called);
|
||||
ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle));
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !defined(_WIN32) */
|
||||
Vendored
+186
-14
@@ -36,11 +36,21 @@
|
||||
#endif
|
||||
|
||||
static uv_fs_event_t fs_event;
|
||||
static const char file_prefix[] = "fsevent-";
|
||||
static uv_timer_t timer;
|
||||
static int timer_cb_called = 0;
|
||||
static int close_cb_called = 0;
|
||||
static int fs_event_cb_called = 0;
|
||||
static int timer_cb_touch_called = 0;
|
||||
static int timer_cb_called;
|
||||
static int close_cb_called;
|
||||
static const int fs_event_file_count = 128;
|
||||
static int fs_event_created;
|
||||
static int fs_event_cb_called;
|
||||
#if defined(PATH_MAX)
|
||||
static char fs_event_filename[PATH_MAX];
|
||||
#else
|
||||
static char fs_event_filename[1024];
|
||||
#endif /* defined(PATH_MAX) */
|
||||
static int timer_cb_touch_called;
|
||||
|
||||
static void fs_event_unlink_files(uv_timer_t* handle, int status);
|
||||
|
||||
static void create_dir(uv_loop_t* loop, const char* name) {
|
||||
int r;
|
||||
@@ -107,6 +117,69 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
|
||||
const char* filename,
|
||||
int events,
|
||||
int status) {
|
||||
fs_event_cb_called++;
|
||||
ASSERT(handle == &fs_event);
|
||||
ASSERT(status == 0);
|
||||
ASSERT(events == UV_RENAME);
|
||||
ASSERT(filename == NULL ||
|
||||
strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0);
|
||||
|
||||
/* Stop watching dir when received events about all files:
|
||||
* both create and close events */
|
||||
if (fs_event_cb_called == 2 * fs_event_file_count) {
|
||||
ASSERT(0 == uv_fs_event_stop(handle));
|
||||
uv_close((uv_handle_t*) handle, close_cb);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* fs_event_get_filename(int i) {
|
||||
snprintf(fs_event_filename,
|
||||
sizeof(fs_event_filename),
|
||||
"watch_dir/%s%d",
|
||||
file_prefix,
|
||||
i);
|
||||
return fs_event_filename;
|
||||
}
|
||||
|
||||
static void fs_event_create_files(uv_timer_t* handle, int status) {
|
||||
int i;
|
||||
|
||||
/* Already created all files */
|
||||
if (fs_event_created == fs_event_file_count) {
|
||||
uv_close((uv_handle_t*) &timer, close_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create all files */
|
||||
for (i = 0; i < 16; i++, fs_event_created++)
|
||||
create_file(handle->loop, fs_event_get_filename(i));
|
||||
|
||||
/* And unlink them */
|
||||
ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 50, 0));
|
||||
}
|
||||
|
||||
void fs_event_unlink_files(uv_timer_t* handle, int status) {
|
||||
int r;
|
||||
int i;
|
||||
|
||||
/* NOTE: handle might be NULL if invoked not as timer callback */
|
||||
|
||||
/* Unlink all files */
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = remove(fs_event_get_filename(i));
|
||||
if (handle != NULL)
|
||||
ASSERT(r == 0);
|
||||
}
|
||||
|
||||
/* And create them again */
|
||||
if (handle != NULL)
|
||||
ASSERT(0 == uv_timer_start(&timer, fs_event_create_files, 50, 0));
|
||||
}
|
||||
|
||||
static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename,
|
||||
int events, int status) {
|
||||
++fs_event_cb_called;
|
||||
@@ -148,12 +221,6 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
|
||||
}
|
||||
}
|
||||
|
||||
static void timer_cb_dir(uv_timer_t* handle, int status) {
|
||||
++timer_cb_called;
|
||||
create_file(handle->loop, "watch_dir/file1");
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static void timer_cb_file(uv_timer_t* handle, int status) {
|
||||
++timer_cb_called;
|
||||
|
||||
@@ -184,6 +251,7 @@ TEST_IMPL(fs_event_watch_dir) {
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
fs_event_unlink_files(NULL, 0);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
@@ -191,20 +259,21 @@ TEST_IMPL(fs_event_watch_dir) {
|
||||
|
||||
r = uv_fs_event_init(loop, &fs_event);
|
||||
ASSERT(r == 0);
|
||||
r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_dir", 0);
|
||||
r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0);
|
||||
ASSERT(r == 0);
|
||||
r = uv_timer_init(loop, &timer);
|
||||
ASSERT(r == 0);
|
||||
r = uv_timer_start(&timer, timer_cb_dir, 100, 0);
|
||||
r = uv_timer_start(&timer, fs_event_create_files, 100, 0);
|
||||
ASSERT(r == 0);
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT(fs_event_cb_called == 1);
|
||||
ASSERT(timer_cb_called == 1);
|
||||
ASSERT(fs_event_cb_called == 2 * fs_event_file_count);
|
||||
ASSERT(fs_event_created == fs_event_file_count);
|
||||
ASSERT(close_cb_called == 2);
|
||||
|
||||
/* Cleanup */
|
||||
fs_event_unlink_files(NULL, 0);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
@@ -556,3 +625,106 @@ TEST_IMPL(fs_event_start_and_close) {
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
static int fs_event_error_reported;
|
||||
|
||||
static void fs_event_error_report_cb(uv_fs_event_t* handle,
|
||||
const char* filename,
|
||||
int events,
|
||||
int status) {
|
||||
if (status != 0)
|
||||
fs_event_error_reported = status;
|
||||
}
|
||||
|
||||
static void timer_cb_nop(uv_timer_t* handle, int status) {
|
||||
++timer_cb_called;
|
||||
uv_close((uv_handle_t*) handle, close_cb);
|
||||
}
|
||||
|
||||
static void fs_event_error_report_close_cb(uv_handle_t* handle) {
|
||||
ASSERT(handle != NULL);
|
||||
close_cb_called++;
|
||||
|
||||
/* handle is allocated on-stack, no need to free it */
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(fs_event_error_reporting) {
|
||||
unsigned int i;
|
||||
uv_loop_t* loops[1024];
|
||||
uv_fs_event_t events[ARRAY_SIZE(loops)];
|
||||
uv_loop_t* loop;
|
||||
uv_fs_event_t* event;
|
||||
|
||||
TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
|
||||
|
||||
remove("watch_dir/");
|
||||
create_dir(uv_default_loop(), "watch_dir");
|
||||
|
||||
/* Create a lot of loops, and start FSEventStream in each of them.
|
||||
* Eventually, this should create enough streams to make FSEventStreamStart()
|
||||
* fail.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(loops); i++) {
|
||||
loop = uv_loop_new();
|
||||
event = &events[i];
|
||||
ASSERT(loop != NULL);
|
||||
|
||||
loops[i] = loop;
|
||||
timer_cb_called = 0;
|
||||
close_cb_called = 0;
|
||||
ASSERT(0 == uv_fs_event_init(loop, event));
|
||||
ASSERT(0 == uv_fs_event_start(event,
|
||||
fs_event_error_report_cb,
|
||||
"watch_dir",
|
||||
0));
|
||||
uv_unref((uv_handle_t*) event);
|
||||
|
||||
/* Let loop run for some time */
|
||||
ASSERT(0 == uv_timer_init(loop, &timer));
|
||||
ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0));
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT(1 == timer_cb_called);
|
||||
ASSERT(1 == close_cb_called);
|
||||
if (fs_event_error_reported != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* At least one loop should fail */
|
||||
ASSERT(fs_event_error_reported == UV_EMFILE);
|
||||
|
||||
/* Stop and close all events, and destroy loops */
|
||||
do {
|
||||
loop = loops[i];
|
||||
event = &events[i];
|
||||
|
||||
ASSERT(0 == uv_fs_event_stop(event));
|
||||
uv_ref((uv_handle_t*) event);
|
||||
uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
|
||||
|
||||
close_cb_called = 0;
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT(close_cb_called == 1);
|
||||
|
||||
uv_loop_delete(loop);
|
||||
|
||||
loops[i] = NULL;
|
||||
} while (i-- != 0);
|
||||
|
||||
remove("watch_dir/");
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !defined(__APPLE__) */
|
||||
|
||||
TEST_IMPL(fs_event_error_reporting) {
|
||||
/* No-op, needed only for FSEvents backend */
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* defined(__APPLE__) */
|
||||
Vendored
+9
-4
@@ -65,6 +65,7 @@ TEST_DECLARE (tcp_connect_error_fault)
|
||||
TEST_DECLARE (tcp_connect_timeout)
|
||||
TEST_DECLARE (tcp_close_while_connecting)
|
||||
TEST_DECLARE (tcp_close)
|
||||
TEST_DECLARE (tcp_close_accept)
|
||||
TEST_DECLARE (tcp_flags)
|
||||
TEST_DECLARE (tcp_write_to_half_open_connection)
|
||||
TEST_DECLARE (tcp_unexpected_read)
|
||||
@@ -108,6 +109,7 @@ TEST_DECLARE (idle_starvation)
|
||||
TEST_DECLARE (loop_handles)
|
||||
TEST_DECLARE (get_loadavg)
|
||||
TEST_DECLARE (walk_handles)
|
||||
TEST_DECLARE (watcher_cross_stop)
|
||||
TEST_DECLARE (ref)
|
||||
TEST_DECLARE (idle_ref)
|
||||
TEST_DECLARE (async_ref)
|
||||
@@ -194,6 +196,7 @@ TEST_DECLARE (fs_event_immediate_close)
|
||||
TEST_DECLARE (fs_event_close_with_pending_event)
|
||||
TEST_DECLARE (fs_event_close_in_callback)
|
||||
TEST_DECLARE (fs_event_start_and_close)
|
||||
TEST_DECLARE (fs_event_error_reporting)
|
||||
TEST_DECLARE (fs_readdir_empty_dir)
|
||||
TEST_DECLARE (fs_readdir_file)
|
||||
TEST_DECLARE (fs_open_dir)
|
||||
@@ -209,8 +212,6 @@ TEST_DECLARE (thread_local_storage)
|
||||
TEST_DECLARE (thread_mutex)
|
||||
TEST_DECLARE (thread_rwlock)
|
||||
TEST_DECLARE (thread_create)
|
||||
TEST_DECLARE (strlcpy)
|
||||
TEST_DECLARE (strlcat)
|
||||
TEST_DECLARE (dlerror)
|
||||
TEST_DECLARE (poll_duplex)
|
||||
TEST_DECLARE (poll_unidirectional)
|
||||
@@ -224,6 +225,7 @@ TEST_DECLARE (listen_with_simultaneous_accepts)
|
||||
TEST_DECLARE (listen_no_simultaneous_accepts)
|
||||
TEST_DECLARE (fs_stat_root)
|
||||
#else
|
||||
TEST_DECLARE (close_fd)
|
||||
TEST_DECLARE (spawn_setuid_setgid)
|
||||
TEST_DECLARE (we_get_signal)
|
||||
TEST_DECLARE (we_get_signals)
|
||||
@@ -306,6 +308,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (tcp_connect_timeout)
|
||||
TEST_ENTRY (tcp_close_while_connecting)
|
||||
TEST_ENTRY (tcp_close)
|
||||
TEST_ENTRY (tcp_close_accept)
|
||||
TEST_ENTRY (tcp_flags)
|
||||
TEST_ENTRY (tcp_write_to_half_open_connection)
|
||||
TEST_ENTRY (tcp_unexpected_read)
|
||||
@@ -396,6 +399,8 @@ TASK_LIST_START
|
||||
TEST_ENTRY (loop_handles)
|
||||
TEST_ENTRY (walk_handles)
|
||||
|
||||
TEST_ENTRY (watcher_cross_stop)
|
||||
|
||||
TEST_ENTRY (active)
|
||||
|
||||
TEST_ENTRY (embed)
|
||||
@@ -452,6 +457,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (listen_no_simultaneous_accepts)
|
||||
TEST_ENTRY (fs_stat_root)
|
||||
#else
|
||||
TEST_ENTRY (close_fd)
|
||||
TEST_ENTRY (spawn_setuid_setgid)
|
||||
TEST_ENTRY (we_get_signal)
|
||||
TEST_ENTRY (we_get_signals)
|
||||
@@ -490,6 +496,7 @@ TASK_LIST_START
|
||||
TEST_ENTRY (fs_event_close_with_pending_event)
|
||||
TEST_ENTRY (fs_event_close_in_callback)
|
||||
TEST_ENTRY (fs_event_start_and_close)
|
||||
TEST_ENTRY (fs_event_error_reporting)
|
||||
TEST_ENTRY (fs_readdir_empty_dir)
|
||||
TEST_ENTRY (fs_readdir_file)
|
||||
TEST_ENTRY (fs_open_dir)
|
||||
@@ -505,8 +512,6 @@ TASK_LIST_START
|
||||
TEST_ENTRY (thread_mutex)
|
||||
TEST_ENTRY (thread_rwlock)
|
||||
TEST_ENTRY (thread_create)
|
||||
TEST_ENTRY (strlcpy)
|
||||
TEST_ENTRY (strlcat)
|
||||
TEST_ENTRY (dlerror)
|
||||
TEST_ENTRY (ip6_addr_link_local)
|
||||
#if 0
|
||||
|
||||
Vendored
+14
-36
@@ -51,7 +51,6 @@ static void close_cb(uv_handle_t* handle) {
|
||||
close_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
static void exit_cb(uv_process_t* process,
|
||||
int64_t exit_status,
|
||||
int term_signal) {
|
||||
@@ -63,29 +62,10 @@ static void exit_cb(uv_process_t* process,
|
||||
}
|
||||
|
||||
|
||||
static void expect(uv_process_t* process,
|
||||
int64_t exit_status,
|
||||
int term_signal,
|
||||
int err) {
|
||||
printf("exit_cb\n");
|
||||
exit_cb_called++;
|
||||
ASSERT(exit_status == err);
|
||||
ASSERT(term_signal == 0);
|
||||
uv_close((uv_handle_t*)process, close_cb);
|
||||
}
|
||||
|
||||
|
||||
static void exit_cb_expect_enoent(uv_process_t* process,
|
||||
int64_t exit_status,
|
||||
int term_signal) {
|
||||
expect(process, exit_status, term_signal, UV_ENOENT);
|
||||
}
|
||||
|
||||
|
||||
static void exit_cb_expect_eperm(uv_process_t* process,
|
||||
int64_t exit_status,
|
||||
int term_signal) {
|
||||
expect(process, exit_status, term_signal, UV_EPERM);
|
||||
static void fail_cb(uv_process_t* process,
|
||||
int64_t exit_status,
|
||||
int term_signal) {
|
||||
ASSERT(0 && "fail_cb called");
|
||||
}
|
||||
|
||||
|
||||
@@ -166,12 +146,12 @@ static void timer_cb(uv_timer_t* handle, int status) {
|
||||
|
||||
|
||||
TEST_IMPL(spawn_fails) {
|
||||
init_process_options("", exit_cb_expect_enoent);
|
||||
init_process_options("", fail_cb);
|
||||
options.file = options.args[0] = "program-that-had-better-not-exist";
|
||||
ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options));
|
||||
ASSERT(1 == uv_is_active((uv_handle_t*) &process));
|
||||
|
||||
ASSERT(UV_ENOENT == uv_spawn(uv_default_loop(), &process, &options));
|
||||
ASSERT(0 == uv_is_active((uv_handle_t*) &process));
|
||||
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
|
||||
ASSERT(1 == exit_cb_called);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
@@ -851,19 +831,18 @@ TEST_IMPL(spawn_setuid_fails) {
|
||||
ASSERT(0 == setuid(pw->pw_uid));
|
||||
}
|
||||
|
||||
init_process_options("spawn_helper1", exit_cb_expect_eperm);
|
||||
init_process_options("spawn_helper1", fail_cb);
|
||||
|
||||
options.flags |= UV_PROCESS_SETUID;
|
||||
options.uid = 0;
|
||||
|
||||
r = uv_spawn(uv_default_loop(), &process, &options);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(r == UV_EPERM);
|
||||
|
||||
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
ASSERT(r == 0);
|
||||
|
||||
ASSERT(exit_cb_called == 1);
|
||||
ASSERT(close_cb_called == 1);
|
||||
ASSERT(close_cb_called == 0);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
@@ -883,19 +862,18 @@ TEST_IMPL(spawn_setgid_fails) {
|
||||
ASSERT(0 == setuid(pw->pw_uid));
|
||||
}
|
||||
|
||||
init_process_options("spawn_helper1", exit_cb_expect_eperm);
|
||||
init_process_options("spawn_helper1", fail_cb);
|
||||
|
||||
options.flags |= UV_PROCESS_SETGID;
|
||||
options.gid = 0;
|
||||
|
||||
r = uv_spawn(uv_default_loop(), &process, &options);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(r == UV_EPERM);
|
||||
|
||||
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
ASSERT(r == 0);
|
||||
|
||||
ASSERT(exit_cb_called == 1);
|
||||
ASSERT(close_cb_called == 1);
|
||||
ASSERT(close_cb_called == 0);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
|
||||
Vendored
+183
@@ -0,0 +1,183 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
static struct sockaddr_in addr;
|
||||
static uv_tcp_t tcp_server;
|
||||
static uv_tcp_t tcp_outgoing[2];
|
||||
static uv_tcp_t tcp_incoming[ARRAY_SIZE(tcp_outgoing)];
|
||||
static uv_connect_t connect_reqs[ARRAY_SIZE(tcp_outgoing)];
|
||||
static uv_tcp_t tcp_check;
|
||||
static uv_connect_t tcp_check_req;
|
||||
static uv_write_t write_reqs[ARRAY_SIZE(tcp_outgoing)];
|
||||
static unsigned int got_connections;
|
||||
static unsigned int close_cb_called;
|
||||
static unsigned int write_cb_called;
|
||||
static unsigned int read_cb_called;
|
||||
|
||||
static void close_cb(uv_handle_t* handle) {
|
||||
close_cb_called++;
|
||||
}
|
||||
|
||||
static void write_cb(uv_write_t* req, int status) {
|
||||
ASSERT(status == 0);
|
||||
write_cb_called++;
|
||||
}
|
||||
|
||||
static void connect_cb(uv_connect_t* req, int status) {
|
||||
unsigned int i;
|
||||
uv_buf_t buf;
|
||||
uv_stream_t* outgoing;
|
||||
|
||||
if (req == &tcp_check_req) {
|
||||
ASSERT(status != 0);
|
||||
|
||||
/* Close check and incoming[0], time to finish test */
|
||||
uv_close((uv_handle_t*) &tcp_incoming[0], close_cb);
|
||||
uv_close((uv_handle_t*) &tcp_check, close_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(status == 0);
|
||||
ASSERT(connect_reqs <= req);
|
||||
ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs));
|
||||
i = req - connect_reqs;
|
||||
|
||||
buf = uv_buf_init("x", 1);
|
||||
outgoing = (uv_stream_t*) &tcp_outgoing[i];
|
||||
ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb));
|
||||
}
|
||||
|
||||
static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
|
||||
static char slab[1];
|
||||
buf->base = slab;
|
||||
buf->len = sizeof(slab);
|
||||
}
|
||||
|
||||
static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) {
|
||||
uv_loop_t* loop;
|
||||
unsigned int i;
|
||||
|
||||
/* Only first stream should receive read events */
|
||||
ASSERT(stream == (uv_stream_t*) &tcp_incoming[0]);
|
||||
ASSERT(0 == uv_read_stop(stream));
|
||||
ASSERT(1 == nread);
|
||||
|
||||
loop = stream->loop;
|
||||
read_cb_called++;
|
||||
|
||||
/* Close all active incomings, except current one */
|
||||
for (i = 1; i < got_connections; i++)
|
||||
uv_close((uv_handle_t*) &tcp_incoming[i], close_cb);
|
||||
|
||||
/* Create new fd that should be one of the closed incomings */
|
||||
ASSERT(0 == uv_tcp_init(loop, &tcp_check));
|
||||
ASSERT(0 == uv_tcp_connect(&tcp_check_req,
|
||||
&tcp_check,
|
||||
(const struct sockaddr*) &addr,
|
||||
connect_cb));
|
||||
ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb));
|
||||
|
||||
/* Close server, so no one will connect to it */
|
||||
uv_close((uv_handle_t*) &tcp_server, close_cb);
|
||||
}
|
||||
|
||||
static void connection_cb(uv_stream_t* server, int status) {
|
||||
unsigned int i;
|
||||
uv_tcp_t* incoming;
|
||||
|
||||
ASSERT(server == (uv_stream_t*) &tcp_server);
|
||||
|
||||
/* Ignore tcp_check connection */
|
||||
if (got_connections == ARRAY_SIZE(tcp_incoming))
|
||||
return;
|
||||
|
||||
/* Accept everyone */
|
||||
incoming = &tcp_incoming[got_connections++];
|
||||
ASSERT(0 == uv_tcp_init(server->loop, incoming));
|
||||
ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming));
|
||||
|
||||
if (got_connections != ARRAY_SIZE(tcp_incoming))
|
||||
return;
|
||||
|
||||
/* Once all clients are accepted - start reading */
|
||||
for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) {
|
||||
incoming = &tcp_incoming[i];
|
||||
ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_IMPL(tcp_close_accept) {
|
||||
unsigned int i;
|
||||
uv_loop_t* loop;
|
||||
uv_tcp_t* client;
|
||||
|
||||
/*
|
||||
* A little explanation of what goes on below:
|
||||
*
|
||||
* We'll create server and connect to it using two clients, each writing one
|
||||
* byte once connected.
|
||||
*
|
||||
* When all clients will be accepted by server - we'll start reading from them
|
||||
* and, on first client's first byte, will close second client and server.
|
||||
* After that, we'll immediately initiate new connection to server using
|
||||
* tcp_check handle (thus, reusing fd from second client).
|
||||
*
|
||||
* In this situation uv__io_poll()'s event list should still contain read
|
||||
* event for second client, and, if not cleaned up properly, `tcp_check` will
|
||||
* receive stale event of second incoming and invoke `connect_cb` with zero
|
||||
* status.
|
||||
*/
|
||||
|
||||
loop = uv_default_loop();
|
||||
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
|
||||
|
||||
ASSERT(0 == uv_tcp_init(loop, &tcp_server));
|
||||
ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr));
|
||||
ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server,
|
||||
ARRAY_SIZE(tcp_outgoing),
|
||||
connection_cb));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) {
|
||||
client = tcp_outgoing + i;
|
||||
|
||||
ASSERT(0 == uv_tcp_init(loop, client));
|
||||
ASSERT(0 == uv_tcp_connect(&connect_reqs[i],
|
||||
client,
|
||||
(const struct sockaddr*) &addr,
|
||||
connect_cb));
|
||||
}
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections);
|
||||
ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called);
|
||||
ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called);
|
||||
ASSERT(1 == read_cb_called);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
Vendored
-97
@@ -1,97 +0,0 @@
|
||||
/* 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 <string.h>
|
||||
|
||||
#define memeq(a, b, c) (memcmp((a), (b), (c)) == 0)
|
||||
|
||||
|
||||
TEST_IMPL(strlcpy) {
|
||||
size_t r;
|
||||
|
||||
{
|
||||
char dst[2] = "A";
|
||||
r = uv_strlcpy(dst, "", 0);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(memeq(dst, "A", 1));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[2] = "A";
|
||||
r = uv_strlcpy(dst, "B", 1);
|
||||
ASSERT(r == 0);
|
||||
ASSERT(memeq(dst, "", 1));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[2] = "A";
|
||||
r = uv_strlcpy(dst, "B", 2);
|
||||
ASSERT(r == 1);
|
||||
ASSERT(memeq(dst, "B", 2));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[3] = "AB";
|
||||
r = uv_strlcpy(dst, "CD", 3);
|
||||
ASSERT(r == 2);
|
||||
ASSERT(memeq(dst, "CD", 3));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(strlcat) {
|
||||
size_t r;
|
||||
|
||||
{
|
||||
char dst[2] = "A";
|
||||
r = uv_strlcat(dst, "B", 1);
|
||||
ASSERT(r == 1);
|
||||
ASSERT(memeq(dst, "A", 2));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[2] = "A";
|
||||
r = uv_strlcat(dst, "B", 2);
|
||||
ASSERT(r == 1);
|
||||
ASSERT(memeq(dst, "A", 2));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[3] = "A";
|
||||
r = uv_strlcat(dst, "B", 3);
|
||||
ASSERT(r == 2);
|
||||
ASSERT(memeq(dst, "AB", 3));
|
||||
}
|
||||
|
||||
{
|
||||
char dst[5] = "AB";
|
||||
r = uv_strlcat(dst, "CD", 5);
|
||||
ASSERT(r == 4);
|
||||
ASSERT(memeq(dst, "ABCD", 5));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
/* 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 <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* NOTE: Number should be big enough to trigger this problem */
|
||||
static uv_udp_t sockets[2500];
|
||||
static uv_udp_send_t reqs[ARRAY_SIZE(sockets)];
|
||||
static char slab[1];
|
||||
static unsigned int recv_cb_called;
|
||||
static unsigned int send_cb_called;
|
||||
static unsigned int close_cb_called;
|
||||
|
||||
static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
|
||||
buf->base = slab;
|
||||
buf->len = sizeof(slab);
|
||||
}
|
||||
|
||||
|
||||
static void recv_cb(uv_udp_t* handle,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf,
|
||||
const struct sockaddr* addr,
|
||||
unsigned flags) {
|
||||
recv_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
static void send_cb(uv_udp_send_t* req, int status) {
|
||||
send_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
static void close_cb(uv_handle_t* handle) {
|
||||
close_cb_called++;
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(watcher_cross_stop) {
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
unsigned int i;
|
||||
struct sockaddr_in addr;
|
||||
uv_buf_t buf;
|
||||
char big_string[1024];
|
||||
|
||||
TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32);
|
||||
|
||||
ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
|
||||
memset(big_string, 'A', sizeof(big_string));
|
||||
buf = uv_buf_init(big_string, sizeof(big_string));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sockets); i++) {
|
||||
ASSERT(0 == uv_udp_init(loop, &sockets[i]));
|
||||
ASSERT(0 == uv_udp_bind(&sockets[i], (const struct sockaddr*) &addr, 0));
|
||||
ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb));
|
||||
ASSERT(0 == uv_udp_send(&reqs[i],
|
||||
&sockets[i],
|
||||
&buf,
|
||||
1,
|
||||
(const struct sockaddr*) &addr,
|
||||
send_cb));
|
||||
}
|
||||
|
||||
while (recv_cb_called == 0)
|
||||
uv_run(loop, UV_RUN_ONCE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sockets); i++)
|
||||
uv_close((uv_handle_t*) &sockets[i], close_cb);
|
||||
|
||||
ASSERT(0 < recv_cb_called && recv_cb_called <= ARRAY_SIZE(sockets));
|
||||
ASSERT(ARRAY_SIZE(sockets) == send_cb_called);
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT(ARRAY_SIZE(sockets) == close_cb_called);
|
||||
|
||||
MAKE_VALGRIND_HAPPY();
|
||||
return 0;
|
||||
}
|
||||
Vendored
+3
-1
@@ -297,12 +297,12 @@
|
||||
'test/runner.h',
|
||||
'test/test-get-loadavg.c',
|
||||
'test/task.h',
|
||||
'test/test-util.c',
|
||||
'test/test-active.c',
|
||||
'test/test-async.c',
|
||||
'test/test-async-null-cb.c',
|
||||
'test/test-callback-stack.c',
|
||||
'test/test-callback-order.c',
|
||||
'test/test-close-fd.c',
|
||||
'test/test-close-order.c',
|
||||
'test/test-connection-fail.c',
|
||||
'test/test-cwd-and-chdir.c',
|
||||
@@ -324,6 +324,7 @@
|
||||
'test/test-loop-handles.c',
|
||||
'test/test-loop-stop.c',
|
||||
'test/test-walk-handles.c',
|
||||
'test/test-watcher-cross-stop.c',
|
||||
'test/test-multiple-listen.c',
|
||||
'test/test-osx-select.c',
|
||||
'test/test-pass-always.c',
|
||||
@@ -348,6 +349,7 @@
|
||||
'test/test-tcp-bind-error.c',
|
||||
'test/test-tcp-bind6-error.c',
|
||||
'test/test-tcp-close.c',
|
||||
'test/test-tcp-close-accept.c',
|
||||
'test/test-tcp-close-while-connecting.c',
|
||||
'test/test-tcp-connect-error-after-write.c',
|
||||
'test/test-tcp-shutdown-after-write.c',
|
||||
|
||||
Vendored
+1
-1
@@ -91,7 +91,7 @@ exit /b 1
|
||||
|
||||
:have_gyp
|
||||
if not defined PYTHON set PYTHON="python"
|
||||
%PYTHON% gyp_uv -Dtarget_arch=%target_arch% -Dlibrary=%library%
|
||||
%PYTHON% gyp_uv.py -Dtarget_arch=%target_arch% -Dlibrary=%library%
|
||||
if errorlevel 1 goto create-msvs-files-failed
|
||||
if not exist uv.sln goto create-msvs-files-failed
|
||||
echo Project files generated.
|
||||
|
||||
Reference in new issue
Block a user