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:
Ben Noordhuis 2011-11-09 13:48:30 +01:00
parent 942c68b80e
commit 4794c12f58
2 changed files with 130 additions and 51 deletions

View File

@ -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;
}

View File

@ -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();