summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_file.c
diff options
context:
space:
mode:
authorIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
committerIgor Sysoev <igor@sysoev.ru>2017-01-17 20:00:00 +0300
commit16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch)
treee6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_file.c
downloadunit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz
unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2
Initial version.
Diffstat (limited to 'src/nxt_file.c')
-rw-r--r--src/nxt_file.c601
1 files changed, 601 insertions, 0 deletions
diff --git a/src/nxt_file.c b/src/nxt_file.c
new file mode 100644
index 00000000..b2cab337
--- /dev/null
+++ b/src/nxt_file.c
@@ -0,0 +1,601 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+
+
+nxt_int_t
+nxt_file_open(nxt_file_t *file, nxt_uint_t mode, nxt_uint_t create,
+ nxt_file_access_t access)
+{
+ nxt_thread_debug(thr);
+
+#ifdef __CYGWIN__
+ mode |= O_BINARY;
+#endif
+
+ /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
+ mode |= (O_NONBLOCK | create);
+
+ file->fd = open((char *) file->name, mode, access);
+
+ file->error = (file->fd == -1) ? nxt_errno : 0;
+
+ nxt_thread_time_debug_update(thr);
+
+ nxt_log_debug(thr->log, "open(\"%FN\", 0x%uXi, 0x%uXi): %FD err:%d",
+ file->name, mode, access, file->fd, file->error);
+
+ if (file->fd != -1) {
+ return NXT_OK;
+ }
+
+ if (file->log_level != 0) {
+ nxt_thread_log_error(file->log_level, "open(\"%FN\") failed %E",
+ file->name, file->error);
+ }
+
+ return NXT_ERROR;
+}
+
+
+void
+nxt_file_close(nxt_file_t *file)
+{
+ nxt_thread_log_debug("close(%FD)", file->fd);
+
+ if (close(file->fd) != 0) {
+ nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD, \"%FN\") failed %E",
+ file->fd, file->name, nxt_errno);
+ }
+}
+
+
+ssize_t
+nxt_file_write(nxt_file_t *file, const u_char *buf, size_t size,
+ nxt_off_t offset)
+{
+ ssize_t n;
+
+ nxt_thread_debug(thr);
+
+ n = pwrite(file->fd, buf, size, offset);
+
+ file->error = (n < 0) ? nxt_errno : 0;
+
+ nxt_thread_time_debug_update(thr);
+
+ nxt_log_debug(thr->log, "pwrite(%FD, %p, %uz, %O): %z",
+ file->fd, buf, size, offset, n);
+
+ if (nxt_fast_path(n >= 0)) {
+ return n;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT,
+ "pwrite(%FD, \"%FN\", %p, %uz, %O) failed %E",
+ file->fd, file->name, buf, size,
+ offset, file->error);
+
+ return NXT_ERROR;
+}
+
+
+ssize_t
+nxt_file_read(nxt_file_t *file, u_char *buf, size_t size, nxt_off_t offset)
+{
+ ssize_t n;
+
+ nxt_thread_debug(thr);
+
+ n = pread(file->fd, buf, size, offset);
+
+ file->error = (n <= 0) ? nxt_errno : 0;
+
+ nxt_thread_time_debug_update(thr);
+
+ nxt_log_debug(thr->log, "pread(%FD, %p, %uz, %O): %z",
+ file->fd, buf, size, offset, n);
+
+ if (nxt_fast_path(n >= 0)) {
+ return n;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT,
+ "pread(%FD, \"%FN\", %p, %uz, %O) failed %E",
+ file->fd, file->name, buf, size,
+ offset, file->error);
+
+ return NXT_ERROR;
+}
+
+
+#if (NXT_HAVE_READAHEAD)
+
+/* FreeBSD 8.0 fcntl(F_READAHEAD, size) enables read ahead up to the size. */
+
+void
+nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
+{
+ int ret;
+ u_char buf;
+
+ ret = fcntl(file->fd, F_READAHEAD, (int) size);
+
+ nxt_thread_log_debug("fcntl(%FD, F_READAHEAD, %uz): %d",
+ file->fd, size, ret);
+
+ if (nxt_fast_path(ret != -1)) {
+ (void) nxt_file_read(file, &buf, 1, offset);
+ return;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT,
+ "fcntl(%FD, \"%FN\", F_READAHEAD, %uz) failed %E",
+ file->fd, file->name, size, nxt_errno);
+}
+
+#elif (NXT_HAVE_POSIX_FADVISE)
+
+/*
+ * POSIX_FADV_SEQUENTIAL
+ * Linux doubles the default readahead window size of a backing device
+ * which is usually 128K.
+ *
+ * FreeBSD does nothing.
+ *
+ * POSIX_FADV_WILLNEED
+ * Linux preloads synchronously up to 2M of specified file region in
+ * the kernel page cache. Linux-specific readahead(2) syscall does
+ * the same. Both operations are blocking despite posix_fadvise(2)
+ * claims the opposite.
+ *
+ * FreeBSD does nothing.
+ */
+
+void
+nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
+{
+ nxt_err_t err;
+
+ err = posix_fadvise(file->fd, offset, size, POSIX_FADV_WILLNEED);
+
+ nxt_thread_log_debug("posix_fadvise(%FD, \"%FN\", %O, %uz, %d): %d",
+ file->fd, file->name, offset, size,
+ POSIX_FADV_WILLNEED, err);
+
+ if (nxt_fast_path(err == 0)) {
+ return;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT,
+ "posix_fadvise(%FD, \"%FN\", %O, %uz, %d) failed %E",
+ file->fd, file->name, offset, size,
+ POSIX_FADV_WILLNEED, err);
+}
+
+#elif (NXT_HAVE_RDAHEAD)
+
+/* MacOSX fcntl(F_RDAHEAD). */
+
+void
+nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
+{
+ int ret;
+ u_char buf;
+
+ ret = fcntl(file->fd, F_RDAHEAD, 1);
+
+ nxt_thread_log_debug("fcntl(%FD, F_RDAHEAD, 1): %d", file->fd, ret);
+
+ if (nxt_fast_path(ret != -1)) {
+ (void) nxt_file_read(file, &buf, 1, offset);
+ return;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT,
+ "fcntl(%FD, \"%FN\", F_RDAHEAD, 1) failed %E",
+ file->fd, file->name, nxt_errno);
+}
+
+#else
+
+void
+nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
+{
+ u_char buf;
+
+ (void) nxt_file_read(file, &buf, 1, offset);
+}
+
+#endif
+
+
+nxt_int_t
+nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi)
+{
+ int n;
+
+ if (file->fd == NXT_FILE_INVALID) {
+ n = stat((char *) file->name, fi);
+
+ file->error = (n != 0) ? nxt_errno : 0;
+
+ nxt_thread_log_debug("stat(\"%FN)\": %d", file->name, n);
+
+ if (n == 0) {
+ return NXT_OK;
+ }
+
+ if (file->log_level != 0) {
+ nxt_thread_log_error(file->log_level, "stat(\"%FN\") failed %E",
+ file->name, file->error);
+ }
+
+ return NXT_ERROR;
+
+ } else {
+ n = fstat(file->fd, fi);
+
+ file->error = (n != 0) ? nxt_errno : 0;
+
+ nxt_thread_log_debug("fstat(%FD): %d", file->fd, n);
+
+ if (n == 0) {
+ return NXT_OK;
+ }
+
+ /* Use NXT_LOG_CRIT because fstat() error on open file is strange. */
+
+ nxt_thread_log_error(NXT_LOG_CRIT, "fstat(%FD, \"%FN\") failed %E",
+ file->fd, file->name, file->error);
+
+ return NXT_ERROR;
+ }
+}
+
+
+nxt_int_t
+nxt_file_delete(nxt_file_name_t *name)
+{
+ nxt_thread_log_debug("unlink(\"%FN\")", name);
+
+ if (nxt_fast_path(unlink((char *) name) == 0)) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_alert("unlink(\"%FN\") failed %E", name, nxt_errno);
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access)
+{
+ if (nxt_fast_path(chmod((char *) name, access) == 0)) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_alert("chmod(\"%FN\") failed %E", name, nxt_errno);
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name)
+{
+ int ret;
+
+ nxt_thread_log_debug("rename(\"%FN\", \"%FN\")", old_name, new_name);
+
+ ret = rename((char *) old_name, (char *) new_name);
+ if (nxt_fast_path(ret == 0)) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_alert("rename(\"%FN\", \"%FN\") failed %E",
+ old_name, new_name, nxt_errno);
+
+ return NXT_ERROR;
+}
+
+
+/*
+ * ioctl(FIONBIO) sets a non-blocking mode using one syscall,
+ * thereas fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state
+ * using fcntl(F_GETFL).
+ *
+ * ioctl() and fcntl() are syscalls at least in Linux 2.2, FreeBSD 2.x,
+ * and Solaris 7.
+ *
+ * Linux 2.4 uses BKL for ioctl() and fcntl(F_SETFL).
+ * Linux 2.6 does not use BKL.
+ */
+
+#if (NXT_HAVE_FIONBIO)
+
+nxt_int_t
+nxt_fd_nonblocking(nxt_fd_t fd)
+{
+ int nb;
+
+ nb = 1;
+
+ if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_alert("ioctl(%d, FIONBIO) failed %E", fd, nxt_errno);
+
+ return NXT_ERROR;
+
+}
+
+
+nxt_int_t
+nxt_fd_blocking(nxt_fd_t fd)
+{
+ int nb;
+
+ nb = 0;
+
+ if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_alert("ioctl(%d, !FIONBIO) failed %E", fd, nxt_errno);
+
+ return NXT_ERROR;
+}
+
+#else /* !(NXT_HAVE_FIONBIO) */
+
+nxt_int_t
+nxt_fd_nonblocking(nxt_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (nxt_slow_path(flags == -1)) {
+ nxt_thread_log_alert("fcntl(%d, F_GETFL) failed %E", fd, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ flags |= O_NONBLOCK;
+
+ if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
+ nxt_thread_log_alert("fcntl(%d, F_SETFL, O_NONBLOCK) failed %E",
+ fd, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+nxt_int_t
+nxt_fd_blocking(nxt_fd_t fd)
+{
+ int flags;
+
+ flags = fcntl(fd, F_GETFL);
+
+ if (nxt_slow_path(flags == -1)) {
+ nxt_thread_log_alert("fcntl(%d, F_GETFL) failed %E",
+ fd, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ flags &= O_NONBLOCK;
+
+ if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
+ nxt_thread_log_alert("fcntl(%d, F_SETFL, !O_NONBLOCK) failed %E",
+ fd, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+#endif /* NXT_HAVE_FIONBIO */
+
+
+ssize_t
+nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size)
+{
+ ssize_t n;
+ nxt_err_t err;
+
+ n = write(fd, buf, size);
+
+ err = (n == -1) ? nxt_errno : 0;
+
+ nxt_thread_log_debug("write(%FD, %p, %uz): %z", fd, buf, size, n);
+
+ if (nxt_slow_path(n <= 0)) {
+ nxt_thread_log_alert("write(%FD) failed %E", fd, err);
+ }
+
+ return n;
+}
+
+
+ssize_t
+nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size)
+{
+ ssize_t n;
+ nxt_err_t err;
+
+ n = read(fd, buf, size);
+
+ err = (n == -1) ? nxt_errno : 0;
+
+ nxt_thread_log_debug("read(%FD, %p, %uz): %z", fd, buf, size, n);
+
+ if (nxt_slow_path(n <= 0)) {
+
+ if (err == NXT_EAGAIN) {
+ return 0;
+ }
+
+ nxt_thread_log_alert("read(%FD) failed %E", fd, err);
+ }
+
+ return n;
+}
+
+
+void
+nxt_fd_close(nxt_fd_t fd)
+{
+ nxt_thread_log_debug("close(%FD)", fd);
+
+ if (nxt_slow_path(close(fd) != 0)) {
+ nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD) failed %E",
+ fd, nxt_errno);
+ }
+}
+
+
+/*
+ * nxt_file_redirect() redirects the file to the fd descriptor.
+ * Then the fd descriptor is closed.
+ */
+
+nxt_int_t
+nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd)
+{
+ nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", fd, file->fd, file->name);
+
+ if (dup2(fd, file->fd) == -1) {
+ nxt_thread_log_error(NXT_LOG_CRIT, "dup2(%FD, %FD, \"%FN\") failed %E",
+ fd, file->fd, file->name, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ if (close(fd) != 0) {
+ nxt_thread_log_error(NXT_LOG_CRIT, "close(%FD, \"%FN\") failed %E",
+ fd, file->name, nxt_errno);
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+}
+
+
+/* nxt_file_stderr() redirects the stderr descriptor to the file. */
+
+nxt_int_t
+nxt_file_stderr(nxt_file_t *file)
+{
+ nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")",
+ file->fd, STDERR_FILENO, file->name);
+
+ if (dup2(file->fd, STDERR_FILENO) != -1) {
+ return NXT_OK;
+ }
+
+ nxt_thread_log_error(NXT_LOG_CRIT, "dup2(%FD, %FD, \"%FN\") failed %E",
+ file->fd, STDERR_FILENO, file->name);
+
+ return NXT_ERROR;
+}
+
+
+nxt_int_t
+nxt_stderr_start(void)
+{
+ int flags, fd;
+
+ flags = fcntl(nxt_stderr, F_GETFL);
+
+ if (flags != -1) {
+ /*
+ * If the stderr output of a multithreaded application is
+ * redirected to a file:
+ * Linux, Solaris and MacOSX do not write atomically to the output;
+ * MacOSX besides adds zeroes to the output.
+ * O_APPEND fixes this.
+ */
+ (void) fcntl(nxt_stderr, F_SETFL, flags | O_APPEND);
+
+ } else {
+ /*
+ * The stderr descriptor is closed before application start.
+ * Reserve the stderr descriptor for future use. Errors are
+ * ignored because anyway they could be written nowhere.
+ */
+ fd = open("/dev/null", O_WRONLY | O_APPEND);
+
+ if (fd != -1) {
+ (void) dup2(fd, nxt_stderr);
+
+ if (fd != nxt_stderr) {
+ (void) close(fd);
+ }
+ }
+ }
+
+ return flags;
+}
+
+
+nxt_int_t
+nxt_pipe_create(nxt_fd_t *pp, nxt_bool_t nbread, nxt_bool_t nbwrite)
+{
+ if (pipe(pp) != 0) {
+ nxt_thread_log_alert("pipe() failed %E", nxt_errno);
+
+ return NXT_ERROR;
+ }
+
+ nxt_thread_log_debug("pipe(): %FD:%FD", pp[0], pp[1]);
+
+ if (nbread) {
+ if (nxt_fd_nonblocking(pp[0]) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ if (nbwrite) {
+ if (nxt_fd_nonblocking(pp[1]) != NXT_OK) {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+}
+
+
+void
+nxt_pipe_close(nxt_fd_t *pp)
+{
+ nxt_thread_log_debug("pipe close(%FD:%FD)", pp[0], pp[1]);
+
+ if (close(pp[0]) != 0) {
+ nxt_thread_log_alert("pipe close (%FD) failed %E", pp[0], nxt_errno);
+ }
+
+ if (close(pp[1]) != 0) {
+ nxt_thread_log_alert("pipe close(%FD) failed %E", pp[1], nxt_errno);
+ }
+}
+
+
+size_t
+nxt_dir_current(char *buf, size_t len)
+{
+ if (nxt_fast_path(getcwd(buf, len) != NULL)) {
+ return nxt_strlen(buf);
+ }
+
+ nxt_thread_log_alert("getcwd(%uz) failed %E", len, nxt_errno);
+
+ return 0;
+}