diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 73c9827a741..85605b85949 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -98,7 +98,7 @@ src/ev/ev.o: src/ev/ev.c EIO_CPPFLAGS += $(CPPFLAGS) EIO_CPPFLAGS += -DEIO_CONFIG_H=\"$(EIO_CONFIG)\" -EIO_CPPFLAGS += -DEIO_STACKSIZE=65536 +EIO_CPPFLAGS += -DEIO_STACKSIZE=262144 EIO_CPPFLAGS += -D_GNU_SOURCE src/eio/eio.o: src/eio/eio.c diff --git a/deps/uv/include/uv-unix.h b/deps/uv/include/uv-unix.h index 72c66a9f71f..2f084b27e78 100644 --- a/deps/uv/include/uv-unix.h +++ b/deps/uv/include/uv-unix.h @@ -88,6 +88,7 @@ typedef struct { #define UV_PIPE_PRIVATE_FIELDS \ UV_TCP_PRIVATE_FIELDS \ const char* pipe_fname; /* strdup'ed */ \ + void* pipe_flock; /* UV_PREPARE */ \ diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index d72caa6df83..46b03e1124f 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -508,9 +508,8 @@ typedef struct uv_process_options_s { char** env; char* cwd; /* - * The user should supply pointers to uninitialized uv_pipe_t structs for - * stdio. They will be initialized by uv_spawn. The user is reponsible for - * calling uv_close on them. + * The user should supply pointers to initialized uv_pipe_t structs for + * stdio. The user is reponsible for calling uv_close on them. */ uv_pipe_t* stdin_stream; uv_pipe_t* stdout_stream; diff --git a/deps/uv/src/eio/eio.c b/deps/uv/src/eio/eio.c index 7f48add4019..9464e62ced3 100644 --- a/deps/uv/src/eio/eio.c +++ b/deps/uv/src/eio/eio.c @@ -40,7 +40,7 @@ #include "eio.h" #ifdef EIO_STACKSIZE -# define XTHREAD_STACKSIZE EIO_STACKSIZE +# define X_STACKSIZE EIO_STACKSIZE #endif // For statically-linked pthreads-w32, use: @@ -1270,6 +1270,10 @@ eio__scandir (eio_req *req, etp_worker *self) X_LOCK (wrklock); /* the corresponding closedir is in ETP_WORKER_CLEAR */ self->dirp = dirp = opendir (req->ptr1); + + if (req->flags & EIO_FLAG_PTR1_FREE) + free (req->ptr1); + req->flags |= EIO_FLAG_PTR1_FREE | EIO_FLAG_PTR2_FREE; req->ptr1 = dents = flags ? malloc (dentalloc * sizeof (eio_dirent)) : 0; req->ptr2 = names = malloc (namesalloc); diff --git a/deps/uv/src/uv-unix.c b/deps/uv/src/uv-unix.c index 8d1c9420297..33903e0152d 100644 --- a/deps/uv/src/uv-unix.c +++ b/deps/uv/src/uv-unix.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,11 @@ #include #include /* PATH_MAX */ +#ifdef __sun +# include +# include +#endif + #if defined(__APPLE__) #include /* _NSGetExecutablePath */ #endif @@ -73,6 +79,32 @@ struct uv_ares_data_s { static struct uv_ares_data_s ares_data; +typedef struct { + const char* lockfile; + int lockfd; +} uv_flock_t; + + +/* Create a new advisory file lock for `filename`. + * Call `uv_flock_acquire()` to actually acquire the lock. + */ +int uv_flock_init(uv_flock_t* lock, const char* filename); + +/* Try to acquire the file lock. Returns 0 on success, -1 on error. + * Does not wait for the lock to be released if it is held by another process. + * + * If `locked` is not NULL, the memory pointed it points to is set to 1 if + * the file is locked by another process. Only relevant in error scenarios. + */ +int uv_flock_acquire(uv_flock_t* lock, int* locked); + +/* Release the file lock. Returns 0 on success, -1 on error. + */ +int uv_flock_release(uv_flock_t* lock); + +/* Destroy the file lock. Releases the file lock and associated resources. + */ +int uv_flock_destroy(uv_flock_t* lock); void uv__req_init(uv_req_t*); void uv__next(EV_P_ ev_idle* watcher, int revents); @@ -82,6 +114,7 @@ static uv_err_t uv_err_new(uv_handle_t* handle, int sys_error); static int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); static int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +static int uv_pipe_cleanup(uv_pipe_t* handle); static uv_write_t* uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); static void uv__stream_connect(uv_stream_t*); @@ -241,17 +274,8 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { case UV_NAMED_PIPE: pipe = (uv_pipe_t*)handle; - if (pipe->pipe_fname) { - /* - * Unlink the file system entity before closing the file descriptor. - * Doing it the other way around introduces a race where our process - * unlinks a socket with the same name that's just been created by - * another thread or process. - */ - unlink(pipe->pipe_fname); - free((void*)pipe->pipe_fname); - } - uv_read_stop((uv_stream_t*)pipe); + uv_pipe_cleanup(pipe); + uv_read_stop((uv_stream_t*)handle); ev_io_stop(EV_DEFAULT_ &pipe->write_watcher); break; @@ -1807,6 +1831,7 @@ int uv_pipe_init(uv_pipe_t* handle) { uv_counters()->pipe_init++; handle->type = UV_NAMED_PIPE; + handle->pipe_flock = NULL; /* Only set by listener. */ handle->pipe_fname = NULL; /* Only set by listener. */ ev_init(&handle->write_watcher, uv__stream_io); @@ -1825,13 +1850,16 @@ int uv_pipe_init(uv_pipe_t* handle) { int uv_pipe_bind(uv_pipe_t* handle, const char* name) { struct sockaddr_un sun; const char* pipe_fname; + uv_flock_t* pipe_flock; int saved_errno; + int locked; int sockfd; int status; int bound; saved_errno = errno; pipe_fname = NULL; + pipe_flock = NULL; sockfd = -1; status = -1; bound = 0; @@ -1851,6 +1879,20 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* We've got a copy, don't touch the original any more. */ name = NULL; + /* Create and acquire a file lock for this UNIX socket. */ + if ((pipe_flock = malloc(sizeof *pipe_flock)) == NULL + || uv_flock_init(pipe_flock, pipe_fname) == -1) { + uv_err_new((uv_handle_t*)handle, ENOMEM); + goto out; + } + + if (uv_flock_acquire(pipe_flock, &locked) == -1) { + /* Another process holds the lock so the socket is in use. */ + uv_err_new_artificial((uv_handle_t*)handle, + locked ? UV_EADDRINUSE : UV_EACCESS); + goto out; + } + if ((sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { uv_err_new((uv_handle_t*)handle, errno); goto out; @@ -1861,17 +1903,13 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { sun.sun_family = AF_UNIX; if (bind(sockfd, (struct sockaddr*)&sun, sizeof sun) == -1) { -#ifdef DONT_RACE_ME_BRO - /* - * Try to bind the socket. Note that we explicitly don't act - * on EADDRINUSE. Unlinking and trying to bind again opens - * a window for races with other threads and processes. - */ - uv_err_new((uv_handle_t*)handle, (errno == ENOENT) ? EACCES : errno); - goto out; -#else - /* - * Try to re-purpose the socket. This is a potential race window. + /* On EADDRINUSE: + * + * We hold the file lock so there is no other process listening + * on the socket. Ergo, it's stale - remove it. + * + * This assumes that the other process uses locking too + * but that's a good enough assumption for now. */ if (errno != EADDRINUSE || unlink(pipe_fname) == -1 @@ -1880,7 +1918,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { uv_err_new((uv_handle_t*)handle, (errno == ENOENT) ? EACCES : errno); goto out; } -#endif } bound = 1; @@ -1898,6 +1935,11 @@ out: unlink(pipe_fname); } uv__close(sockfd); + + if (pipe_flock) { + uv_flock_destroy(pipe_flock); + } + free((void*)pipe_fname); } @@ -1914,7 +1956,7 @@ static int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { status = -1; if (handle->fd == -1) { - uv_err_new_artificial((uv_handle_t*)handle, UV_ENOTCONN); + uv_err_new_artificial((uv_handle_t*)handle, UV_EINVAL); goto out; } assert(handle->fd >= 0); @@ -1933,6 +1975,37 @@ out: } +static int uv_pipe_cleanup(uv_pipe_t* handle) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (handle->pipe_fname) { + /* + * Unlink the file system entity before closing the file descriptor. + * Doing it the other way around introduces a race where our process + * unlinks a socket with the same name that's just been created by + * another thread or process. + * + * This is less of an issue now that we attach a file lock + * to the socket but it's still a best practice. + */ + unlink(handle->pipe_fname); + free((void*)handle->pipe_fname); + } + + if (handle->pipe_flock) { + uv_flock_destroy((uv_flock_t*)handle->pipe_flock); + free(handle->pipe_flock); + } + + errno = saved_errno; + return status; +} + + int uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, @@ -2198,17 +2271,37 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { process->exit_cb = options.exit_cb; + if (options.stdin_stream) { + if (options.stdin_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } - if (options.stdin_stream && pipe(stdin_pipe) < 0) { - goto error; + if (pipe(stdin_pipe) < 0) { + goto error; + } } - if (options.stdout_stream && pipe(stdout_pipe) < 0) { - goto error; + if (options.stdout_stream) { + if (options.stdout_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stdout_pipe) < 0) { + goto error; + } } - if (options.stderr_stream && pipe(stderr_pipe) < 0) { - goto error; + if (options.stderr_stream) { + if (options.stderr_stream->type != UV_NAMED_PIPE) { + errno = EINVAL; + goto error; + } + + if (pipe(stderr_pipe) < 0) { + goto error; + } } pid = fork(); @@ -2262,7 +2355,6 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { assert(stdin_pipe[0] >= 0); close(stdin_pipe[0]); uv__nonblock(stdin_pipe[1], 1); - uv_pipe_init(options.stdin_stream); uv__stream_open((uv_stream_t*)options.stdin_stream, stdin_pipe[1]); } @@ -2271,7 +2363,6 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { assert(stdout_pipe[1] >= 0); close(stdout_pipe[1]); uv__nonblock(stdout_pipe[0], 1); - uv_pipe_init(options.stdout_stream); uv__stream_open((uv_stream_t*)options.stdout_stream, stdout_pipe[0]); } @@ -2280,7 +2371,6 @@ int uv_spawn(uv_process_t* process, uv_process_options_t options) { assert(stderr_pipe[1] >= 0); close(stderr_pipe[1]); uv__nonblock(stderr_pipe[0], 1); - uv_pipe_init(options.stderr_stream); uv__stream_open((uv_stream_t*)options.stderr_stream, stderr_pipe[0]); } @@ -2308,3 +2398,125 @@ int uv_process_kill(uv_process_t* process, int signum) { return 0; } } + + +#define LOCKFILE_SUFFIX ".lock" +int uv_flock_init(uv_flock_t* lock, const char* filename) { + int saved_errno; + int status; + char* lockfile; + + saved_errno = errno; + status = -1; + + lock->lockfd = -1; + lock->lockfile = NULL; + + if ((lockfile = malloc(strlen(filename) + sizeof LOCKFILE_SUFFIX)) == NULL) { + goto out; + } + + strcpy(lockfile, filename); + strcat(lockfile, LOCKFILE_SUFFIX); + lock->lockfile = lockfile; + status = 0; + +out: + errno = saved_errno; + return status; +} +#undef LOCKFILE_SUFFIX + + +int uv_flock_acquire(uv_flock_t* lock, int* locked_p) { + char buf[32]; + int saved_errno; + int status; + int lockfd; + int locked; + + saved_errno = errno; + status = -1; + lockfd = -1; + locked = 0; + + do { + lockfd = open(lock->lockfile, O_WRONLY | O_CREAT, 0666); + } + while (lockfd == -1 && errno == EINTR); + + if (lockfd == -1) { + goto out; + } + + do { + status = flock(lockfd, LOCK_EX | LOCK_NB); + } + while (status == -1 && errno == EINTR); + + if (status == -1) { + locked = (errno == EAGAIN); /* Lock is held by another process. */ + goto out; + } + + snprintf(buf, sizeof buf, "%d\n", getpid()); + do { + status = write(lockfd, buf, strlen(buf)); + } + while (status == -1 && errno == EINTR); + + lock->lockfd = lockfd; + status = 0; + +out: + if (status) { + uv__close(lockfd); + } + + if (locked_p) { + *locked_p = locked; + } + + errno = saved_errno; + return status; +} + + +int uv_flock_release(uv_flock_t* lock) { + int saved_errno; + int status; + + saved_errno = errno; + status = -1; + + if (unlink(lock->lockfile) == -1) { + /* Now what? */ + goto out; + } + + uv__close(lock->lockfd); + lock->lockfd = -1; + status = 0; + +out: + errno = saved_errno; + return status; +} + + +int uv_flock_destroy(uv_flock_t* lock) { + int saved_errno; + int status; + + saved_errno = errno; + status = unlink(lock->lockfile); + + uv__close(lock->lockfd); + lock->lockfd = -1; + + free((void*)lock->lockfile); + lock->lockfile = NULL; + + errno = saved_errno; + return status; +} diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index 7c98ab0effa..ee627c8879f 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -444,13 +444,13 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { HANDLE pipeHandle; if (handle->flags & UV_HANDLE_BIND_ERROR) { - LOOP->last_error = handle->error; + uv_set_error(UV_EINVAL, 0); return -1; } if (!(handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_GIVEN_OS_HANDLE)) { - uv_set_error(UV_ENOTCONN, 0); + uv_set_error(UV_EINVAL, 0); return -1; } diff --git a/deps/uv/test/benchmark-pump.c b/deps/uv/test/benchmark-pump.c index 4e7f1d7ace6..c6cd2d14191 100644 --- a/deps/uv/test/benchmark-pump.c +++ b/deps/uv/test/benchmark-pump.c @@ -149,7 +149,6 @@ void read_sockets_close_cb(uv_handle_t* handle) { static void start_stats_collection() { - uv_req_t* req = req_alloc(); int r; /* Show-stats timer */ @@ -184,8 +183,6 @@ static void read_cb(uv_stream_t* stream, ssize_t bytes, uv_buf_t buf) { static void write_cb(uv_write_t* req, int status) { - uv_buf_t* buf = (uv_buf_t*) req->data; - ASSERT(status == 0); req_free((uv_req_t*) req); diff --git a/deps/uv/test/test-pipe-bind-error.c b/deps/uv/test/test-pipe-bind-error.c index 69aaaa20cd4..80c76da8da9 100644 --- a/deps/uv/test/test-pipe-bind-error.c +++ b/deps/uv/test/test-pipe-bind-error.c @@ -64,7 +64,7 @@ TEST_IMPL(pipe_bind_error_addrinuse) { r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_EADDRINUSE); + ASSERT(uv_last_error().code == UV_EINVAL); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); @@ -136,7 +136,7 @@ TEST_IMPL(pipe_listen_without_bind) { r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); ASSERT(r == -1); - ASSERT(uv_last_error().code == UV_ENOTCONN); + ASSERT(uv_last_error().code == UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 9449af7cb92..1ad274ef780 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -114,6 +114,8 @@ TEST_IMPL(spawn_stdout) { uv_init(); init_process_options("spawn_helper2"); + + uv_pipe_init(&out); options.stdout_stream = &out; r = uv_spawn(&process, options);