Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

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.
  • Loading branch information...
bnoordhuis committed Nov 9, 2011
1 parent 942c68b commit 4794c12f5886e8d05658fdecdbce4b4a572340ea
Showing with 128 additions and 49 deletions.
  1. +42 −19 src/unix/core.c
  2. +86 −30 src/unix/process.c
@@ -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 (sockfd != -1)
goto out;

if (uv__nonblock(sockfd, 1) == -1 || uv__cloexec(sockfd, 1) == -1) {
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);
return -1;
sockfd = -1;
}

out:
return sockfd;
#endif
}


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

0 comments on commit 4794c12

Please sign in to comment.
You can’t perform that action at this time.