|
|
@ -35,6 +35,7 @@ |
|
|
|
#include <unistd.h> |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/stat.h> |
|
|
|
#include <sys/file.h> |
|
|
|
#include <fcntl.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <sys/un.h> |
|
|
@ -42,6 +43,11 @@ |
|
|
|
#include <arpa/inet.h> |
|
|
|
#include <limits.h> /* PATH_MAX */ |
|
|
|
|
|
|
|
#ifdef __sun |
|
|
|
# include <sys/types.h> |
|
|
|
# include <sys/wait.h> |
|
|
|
#endif |
|
|
|
|
|
|
|
#if defined(__APPLE__) |
|
|
|
#include <mach-o/dyld.h> /* _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,19 +2271,39 @@ 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 (pipe(stdin_pipe) < 0) { |
|
|
|
goto error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (options.stdin_stream && pipe(stdin_pipe) < 0) { |
|
|
|
if (options.stdout_stream) { |
|
|
|
if (options.stdout_stream->type != UV_NAMED_PIPE) { |
|
|
|
errno = EINVAL; |
|
|
|
goto error; |
|
|
|
} |
|
|
|
|
|
|
|
if (options.stdout_stream && pipe(stdout_pipe) < 0) { |
|
|
|
if (pipe(stdout_pipe) < 0) { |
|
|
|
goto error; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (options.stderr_stream && pipe(stderr_pipe) < 0) { |
|
|
|
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(); |
|
|
|
|
|
|
|
if (pid == 0) { |
|
|
@ -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; |
|
|
|
} |
|
|
|