void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) { uv__handle_init(loop, (uv_handle_t*)stream, type); stream->read_cb = NULL; stream->read2_cb = NULL; stream->alloc_cb = NULL; stream->close_cb = NULL; stream->connection_cb = NULL; stream->connect_req = NULL; stream->shutdown_req = NULL; stream->accepted_fd = -1; stream->delayed_error = 0; ngx_queue_init(&stream->write_queue); ngx_queue_init(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) loop->emfile_fd = uv__open_cloexec("/", O_RDONLY); #if defined(__APPLE__) stream->select = NULL; #endif /* defined(__APPLE_) */ uv__io_init(&stream->io_watcher, uv__stream_io, -1); }
/* Implements a best effort approach to mitigating accept() EMFILE errors. * We have a spare file descriptor stashed away that we close to get below * the EMFILE limit. Next, we accept all pending connections and close them * immediately to signal the clients that we're overloaded - and we are, but * we still keep on trucking. * * There is one caveat: it's not reliable in a multi-threaded environment. * The file descriptor limit is per process. Our party trick fails if another * thread opens a file or creates a socket in the time window between us * calling close() and accept(). */ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { int fd; int r; if (loop->emfile_fd == -1) return -1; close(loop->emfile_fd); for (;;) { fd = uv__accept(accept_fd); if (fd != -1) { close(fd); continue; } if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) r = 0; else r = -1; loop->emfile_fd = uv__open_cloexec("/", O_RDONLY); return r; } }
void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) { int err; uv__handle_init(loop, (uv_handle_t*)stream, type); stream->read_cb = NULL; stream->alloc_cb = NULL; stream->close_cb = NULL; stream->connection_cb = NULL; stream->connect_req = NULL; stream->shutdown_req = NULL; stream->accepted_fd = -1; stream->queued_fds = NULL; stream->delayed_error = 0; QUEUE_INIT(&stream->write_queue); QUEUE_INIT(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) { err = uv__open_cloexec("/", O_RDONLY); //if (err >= 0) if (err > 0) // 0 is invalid loop->emfile_fd = err; } uv__io_init(&stream->io_watcher, uv__stream_io, -1); }
/* get a file pointer to a file in read-only and close-on-exec mode */ FILE* uv__open_file(const char* path) { int fd; FILE* fp; fd = uv__open_cloexec(path, O_RDONLY); if (fd < 0) return NULL; fp = fdopen(fd, "r"); if (fp == NULL) uv__close(fd); return fp; }
void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, uv_handle_type type) { int err; uv__handle_init(loop, (uv_handle_t*)stream, type); stream->read_cb = NULL; stream->alloc_cb = NULL; stream->close_cb = NULL; stream->connection_cb = NULL; stream->connect_req = NULL; stream->shutdown_req = NULL; stream->accepted_fd = -1; stream->queued_fds = NULL; stream->delayed_error = 0; QUEUE_INIT(&stream->write_queue); QUEUE_INIT(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) { err = uv__open_cloexec("/dev/null", O_RDONLY); if (err < 0) /* In the rare case that "/dev/null" isn't mounted open "/" * instead. */ err = uv__open_cloexec("/", O_RDONLY); if (err >= 0) loop->emfile_fd = err; } #if defined(__APPLE__) stream->select = NULL; #endif /* defined(__APPLE_) */ uv__io_init(&stream->io_watcher, uv__stream_io, -1); }
int uv__async_start(uv_loop_t* loop, struct uv__async* wa, uv__async_cb cb) { int pipefd[2]; int err; if (wa->io_watcher.fd != -1) return 0; err = uv__async_eventfd(); if (err >= 0) { pipefd[0] = err; pipefd[1] = -1; } else if (err == -ENOSYS) { err = uv__make_pipe(pipefd, UV__F_NONBLOCK); #if defined(__linux__) /* Save a file descriptor by opening one of the pipe descriptors as * read/write through the procfs. That file descriptor can then * function as both ends of the pipe. */ if (err == 0) { char buf[32]; int fd; snprintf(buf, sizeof(buf), "/proc/self/fd/%d", pipefd[0]); fd = uv__open_cloexec(buf, O_RDWR); if (fd >= 0) { uv__close(pipefd[0]); uv__close(pipefd[1]); pipefd[0] = fd; pipefd[1] = fd; } } #endif } if (err < 0) return err; uv__io_init(&wa->io_watcher, uv__async_io, pipefd[0]); uv__io_start(loop, &wa->io_watcher, POLLIN); wa->wfd = pipefd[1]; wa->cb = cb; return 0; }
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { int flags; int newfd; int r; flags = 0; newfd = -1; uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); /* Reopen the file descriptor when it refers to a tty. This lets us put the * tty in non-blocking mode without affecting other processes that share it * with us. * * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also * affects fd 1 of `cat` because both file descriptors refer to the same * struct file in the kernel. When we reopen our fd 0, it points to a * different struct file, hence changing its properties doesn't affect * other processes. */ if (isatty(fd)) { r = uv__open_cloexec("/dev/tty", O_RDWR); if (r < 0) { /* fallback to using blocking writes */ if (!readable) flags |= UV_STREAM_BLOCKING; goto skip; } newfd = r; r = uv__dup2_cloexec(newfd, fd); if (r < 0 && r != -EINVAL) { /* EINVAL means newfd == fd which could conceivably happen if another * thread called close(fd) between our calls to isatty() and open(). * That's a rather unlikely event but let's handle it anyway. */ uv__close(newfd); return r; } fd = newfd; } skip: #if defined(__APPLE__) r = uv__stream_try_select((uv_stream_t*) tty, &fd); if (r) { if (newfd != -1) uv__close(newfd); return r; } #endif if (readable) flags |= UV_STREAM_READABLE; else flags |= UV_STREAM_WRITABLE; if (!(flags & UV_STREAM_BLOCKING)) uv__nonblock(fd, 1); uv__stream_open((uv_stream_t*) tty, fd, flags); tty->mode = UV_TTY_MODE_NORMAL; return 0; }
int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int readable) { uv_handle_type type; int flags; int newfd; int r; int saved_flags; char path[256]; /* File descriptors that refer to files cannot be monitored with epoll. * That restriction also applies to character devices like /dev/random * (but obviously not /dev/tty.) */ type = uv_guess_handle(fd); if (type == UV_FILE || type == UV_UNKNOWN_HANDLE) return UV_EINVAL; flags = 0; newfd = -1; /* Reopen the file descriptor when it refers to a tty. This lets us put the * tty in non-blocking mode without affecting other processes that share it * with us. * * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also * affects fd 1 of `cat` because both file descriptors refer to the same * struct file in the kernel. When we reopen our fd 0, it points to a * different struct file, hence changing its properties doesn't affect * other processes. */ if (type == UV_TTY) { /* Reopening a pty in master mode won't work either because the reopened * pty will be in slave mode (*BSD) or reopening will allocate a new * master/slave pair (Linux). Therefore check if the fd points to a * slave device. */ if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0) r = uv__open_cloexec(path, O_RDWR); else r = -1; if (r < 0) { /* fallback to using blocking writes */ if (!readable) flags |= UV_HANDLE_BLOCKING_WRITES; goto skip; } newfd = r; r = uv__dup2_cloexec(newfd, fd); if (r < 0 && r != UV_EINVAL) { /* EINVAL means newfd == fd which could conceivably happen if another * thread called close(fd) between our calls to isatty() and open(). * That's a rather unlikely event but let's handle it anyway. */ uv__close(newfd); return r; } fd = newfd; } #if defined(__APPLE__) /* Save the fd flags in case we need to restore them due to an error. */ do saved_flags = fcntl(fd, F_GETFL); while (saved_flags == -1 && errno == EINTR); if (saved_flags == -1) { if (newfd != -1) uv__close(newfd); return UV__ERR(errno); } #endif /* Pacify the compiler. */ (void) &saved_flags; skip: uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); /* If anything fails beyond this point we need to remove the handle from * the handle queue, since it was added by uv__handle_init in uv_stream_init. */ if (!(flags & UV_HANDLE_BLOCKING_WRITES)) uv__nonblock(fd, 1); #if defined(__APPLE__) r = uv__stream_try_select((uv_stream_t*) tty, &fd); if (r) { int rc = r; if (newfd != -1) uv__close(newfd); QUEUE_REMOVE(&tty->handle_queue); do r = fcntl(fd, F_SETFL, saved_flags); while (r == -1 && errno == EINTR); return rc; } #endif if (readable) flags |= UV_HANDLE_READABLE; else flags |= UV_HANDLE_WRITABLE; uv__stream_open((uv_stream_t*) tty, fd, flags); tty->mode = UV_TTY_MODE_NORMAL; return 0; }