linux: fall back to traditional syscalls if necessary
libuv uses feature checks to determine if newer syscalls like pipe2() are available. This works fine until someone compiles libuv against kernel headers that are newer than the actual kernel our software runs on. Fall back to traditional (but race-y!) syscalls when the kernel reports ENOSYS or EINVAL.
This commit is contained in:
parent
942c68b80e
commit
4794c12f58
@ -686,22 +686,30 @@ void uv_freeaddrinfo(struct addrinfo* ai) {
|
||||
|
||||
/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */
|
||||
int uv__socket(int domain, int type, int protocol) {
|
||||
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
|
||||
return socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
|
||||
#else
|
||||
int sockfd;
|
||||
|
||||
if ((sockfd = socket(domain, type, protocol)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
|
||||
sockfd = socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol);
|
||||
|
||||
if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) {
|
||||
uv__close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
if (sockfd != -1)
|
||||
goto out;
|
||||
|
||||
return sockfd;
|
||||
if (errno != EINVAL)
|
||||
goto out;
|
||||
#endif
|
||||
|
||||
sockfd = socket(domain, type, protocol);
|
||||
|
||||
if (sockfd == -1)
|
||||
goto out;
|
||||
|
||||
if (uv__nonblock(sockfd, 1) || uv__cloexec(sockfd, 1)) {
|
||||
uv__close(sockfd);
|
||||
sockfd = -1;
|
||||
}
|
||||
|
||||
out:
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
|
||||
@ -710,19 +718,34 @@ int uv__accept(int sockfd, struct sockaddr* saddr, socklen_t slen) {
|
||||
|
||||
assert(sockfd >= 0);
|
||||
|
||||
do {
|
||||
#if defined(HAVE_ACCEPT4)
|
||||
while (1) {
|
||||
#if HAVE_ACCEPT4
|
||||
peerfd = accept4(sockfd, saddr, &slen, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
#else
|
||||
if ((peerfd = accept(sockfd, saddr, &slen)) != -1) {
|
||||
if (uv__cloexec(peerfd, 1) == -1 || uv__nonblock(peerfd, 1) == -1) {
|
||||
uv__close(peerfd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (peerfd != -1)
|
||||
break;
|
||||
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
break;
|
||||
#endif
|
||||
|
||||
if ((peerfd = accept(sockfd, saddr, &slen)) == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (uv__cloexec(peerfd, 1) || uv__nonblock(peerfd, 1)) {
|
||||
uv__close(peerfd);
|
||||
peerfd = -1;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
while (peerfd == -1 && errno == EINTR);
|
||||
|
||||
return peerfd;
|
||||
}
|
||||
|
||||
@ -63,30 +63,97 @@ static void uv__chld(EV_P_ ev_child* watcher, int revents) {
|
||||
}
|
||||
|
||||
|
||||
#define UV__F_IPC (1 << 0)
|
||||
#define UV__F_NONBLOCK (1 << 1)
|
||||
|
||||
static int uv__make_socketpair(int fds[2], int flags) {
|
||||
#ifdef SOCK_NONBLOCK
|
||||
int fl;
|
||||
|
||||
fl = SOCK_CLOEXEC;
|
||||
|
||||
if (flags & UV__F_NONBLOCK)
|
||||
fl |= SOCK_NONBLOCK;
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM|fl, 0, fds) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
|
||||
/* errno == EINVAL so maybe the kernel headers lied about
|
||||
* the availability of SOCK_NONBLOCK. This can happen if people
|
||||
* build libuv against newer kernel headers than the kernel
|
||||
* they actually run the software on.
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds))
|
||||
return -1;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__make_pipe(int fds[2], int flags) {
|
||||
#if HAVE_PIPE2
|
||||
int fl;
|
||||
|
||||
fl = O_CLOEXEC;
|
||||
|
||||
if (flags & UV__F_NONBLOCK)
|
||||
fl |= O_NONBLOCK;
|
||||
|
||||
if (pipe2(fds, fl) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != ENOSYS)
|
||||
return -1;
|
||||
|
||||
/* errno == ENOSYS so maybe the kernel headers lied about
|
||||
* the availability of pipe2(). This can happen if people
|
||||
* build libuv against newer kernel headers than the kernel
|
||||
* they actually run the software on.
|
||||
*/
|
||||
#endif
|
||||
|
||||
if (pipe(fds))
|
||||
return -1;
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
if (flags & UV__F_NONBLOCK) {
|
||||
uv__nonblock(fds[0], 1);
|
||||
uv__nonblock(fds[1], 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Used for initializing stdio streams like options.stdin_stream. Returns
|
||||
* zero on success.
|
||||
*/
|
||||
static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2]) {
|
||||
static int uv__process_init_pipe(uv_pipe_t* handle, int fds[2], int flags) {
|
||||
if (handle->type != UV_NAMED_PIPE) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handle->ipc) {
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (pipe(fds) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
uv__cloexec(fds[0], 1);
|
||||
uv__cloexec(fds[1], 1);
|
||||
|
||||
return 0;
|
||||
if (handle->ipc)
|
||||
return uv__make_socketpair(fds, flags);
|
||||
else
|
||||
return uv__make_pipe(fds, flags);
|
||||
}
|
||||
|
||||
|
||||
@ -118,17 +185,17 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
||||
process->exit_cb = options.exit_cb;
|
||||
|
||||
if (options.stdin_stream &&
|
||||
uv__process_init_pipe(options.stdin_stream, stdin_pipe)) {
|
||||
uv__process_init_pipe(options.stdin_stream, stdin_pipe, 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (options.stdout_stream &&
|
||||
uv__process_init_pipe(options.stdout_stream, stdout_pipe)) {
|
||||
uv__process_init_pipe(options.stdout_stream, stdout_pipe, 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (options.stderr_stream &&
|
||||
uv__process_init_pipe(options.stderr_stream, stderr_pipe)) {
|
||||
uv__process_init_pipe(options.stderr_stream, stderr_pipe, 0)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -153,19 +220,8 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
|
||||
* the parent polls the read end until it sees POLLHUP.
|
||||
*/
|
||||
#if SPAWN_WAIT_EXEC
|
||||
# ifdef HAVE_PIPE2
|
||||
if (pipe2(signal_pipe, O_CLOEXEC | O_NONBLOCK) < 0) {
|
||||
if (uv__make_pipe(signal_pipe, UV__F_NONBLOCK))
|
||||
goto error;
|
||||
}
|
||||
# else
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe) < 0) {
|
||||
goto error;
|
||||
}
|
||||
uv__cloexec(signal_pipe[0], 1);
|
||||
uv__cloexec(signal_pipe[1], 1);
|
||||
uv__nonblock(signal_pipe[0], 1);
|
||||
uv__nonblock(signal_pipe[1], 1);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
pid = fork();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user