/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include nxt_int_t nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access) { #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; #if (NXT_DEBUG) nxt_thread_time_update(task->thread); #endif nxt_debug(task, "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_log(task, file->log_level, "open(\"%FN\") failed %E", file->name, file->error); } return NXT_ERROR; } #if (NXT_HAVE_OPENAT2) nxt_int_t nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode, nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd, nxt_uint_t resolve) { struct open_how how; nxt_memzero(&how, sizeof(how)); /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */ mode |= (O_NONBLOCK | create); how.flags = mode; how.mode = access; how.resolve = resolve; file->fd = syscall(SYS_openat2, dfd, file->name, &how, sizeof(how)); file->error = (file->fd == -1) ? nxt_errno : 0; #if (NXT_DEBUG) nxt_thread_time_update(task->thread); #endif nxt_debug(task, "openat2(%FD, \"%FN\"): %FD err:%d", dfd, file->name, file->fd, file->error); if (file->fd != -1) { return NXT_OK; } if (file->log_level != 0) { nxt_log(task, file->log_level, "openat2(%FD, \"%FN\") failed %E", dfd, file->name, file->error); } return NXT_ERROR; } #endif void nxt_file_close(nxt_task_t *task, nxt_file_t *file) { nxt_debug(task, "close(%FD)", file->fd); if (close(file->fd) != 0) { nxt_alert(task, "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_alert("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_alert("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_alert("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_alert("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_alert("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_ALERT because fstat() error on open file is strange. */ nxt_thread_log_alert("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_chown(nxt_file_name_t *name, const char *owner, const char *group) { int err; char *buf; long bufsize; gid_t gid = ~0; uid_t uid = ~0; if (owner == NULL && group == NULL) { return NXT_OK; } if (owner != NULL) { struct passwd pwd, *result; bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); if (bufsize == -1) { bufsize = 32768; } buf = nxt_malloc(bufsize); if (buf == NULL) { return NXT_ERROR; } err = getpwnam_r(owner, &pwd, buf, bufsize, &result); if (result == NULL) { nxt_thread_log_alert("getpwnam_r(\"%s\", ...) failed %E %s", owner, nxt_errno, err == 0 ? "(User not found)" : ""); goto out_err_free; } uid = pwd.pw_uid; nxt_free(buf); } if (group != NULL) { struct group grp, *result; bufsize = sysconf(_SC_GETGR_R_SIZE_MAX); if (bufsize == -1) { bufsize = 32768; } buf = nxt_malloc(bufsize); if (buf == NULL) { return NXT_ERROR; } err = getgrnam_r(group, &grp, buf, bufsize, &result); if (result == NULL) { nxt_thread_log_alert("getgrnam_r(\"%s\", ...) failed %E %s", group, nxt_errno, err == 0 ? "(Group not found)" : ""); goto out_err_free; } gid = grp.gr_gid; nxt_free(buf); } if (nxt_fast_path(chown((const char *) name, uid, gid) == 0)) { return NXT_OK; } nxt_thread_log_alert("chown(\"%FN\", %l, %l) failed %E", name, owner != NULL ? (long) uid : -1, group != NULL ? (long) gid : -1, nxt_errno); return NXT_ERROR; out_err_free: nxt_free(buf); 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_task_t *task, nxt_fd_t fd) { int nb; nb = 1; if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) { return NXT_OK; } nxt_alert(task, "ioctl(%d, FIONBIO) failed %E", fd, nxt_errno); return NXT_ERROR; } nxt_int_t nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd) { int nb; nb = 0; if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) { return NXT_OK; } nxt_alert(task, "ioctl(%d, !FIONBIO) failed %E", fd, nxt_errno); return NXT_ERROR; } #else /* !(NXT_HAVE_FIONBIO) */ nxt_int_t nxt_fd_nonblocking(nxt_task_t *task, nxt_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (nxt_slow_path(flags == -1)) { nxt_alert(task, "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_alert(task, "fcntl(%d, F_SETFL, O_NONBLOCK) failed %E", fd, nxt_errno); return NXT_ERROR; } return NXT_OK; } nxt_int_t nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd) { int flags; flags = fcntl(fd, F_GETFL); if (nxt_slow_path(flags == -1)) { nxt_alert(task, "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_alert(task, "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_alert("close(%FD) failed %E", fd, nxt_errno); } } FILE * nxt_file_fopen(nxt_task_t *task, const char *pathname, const char *mode) { int err; FILE *fp; #if (NXT_DEBUG) nxt_thread_time_update(task->thread); #endif fp = fopen(pathname, mode); err = (fp == NULL) ? nxt_errno : 0; nxt_debug(task, "fopen(\"%s\", \"%s\"): fp:%p err:%d", pathname, mode, fp, err); if (nxt_fast_path(fp != NULL)) { return fp; } nxt_alert(task, "fopen(\"%s\") failed %E", pathname, err); return NULL; } void nxt_file_fclose(nxt_task_t *task, FILE *fp) { nxt_debug(task, "fclose(%p)", fp); if (nxt_slow_path(fclose(fp) == -1)) { nxt_alert(task, "fclose() failed %E", 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_alert("dup2(%FD, %FD, \"%FN\") failed %E", fd, file->fd, file->name, nxt_errno); return NXT_ERROR; } if (close(fd) != 0) { nxt_thread_log_alert("close(%FD, \"%FN\") failed %E", fd, file->name, nxt_errno); return NXT_ERROR; } return NXT_OK; } /* nxt_file_stdout() redirects the stdout descriptor to the file. */ nxt_int_t nxt_file_stdout(nxt_file_t *file) { nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", file->fd, STDOUT_FILENO, file->name); if (dup2(file->fd, STDOUT_FILENO) != -1) { return NXT_OK; } nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E", file->fd, STDOUT_FILENO, file->name, nxt_errno); return NXT_ERROR; } /* 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_alert("dup2(%FD, %FD, \"%FN\") failed %E", file->fd, STDERR_FILENO, file->name, nxt_errno); 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_task_t *task, nxt_fd_t *pp, nxt_bool_t nbread, nxt_bool_t nbwrite) { if (pipe(pp) != 0) { nxt_alert(task, "pipe() failed %E", nxt_errno); return NXT_ERROR; } nxt_debug(task, "pipe(): %FD:%FD", pp[0], pp[1]); if (nbread) { if (nxt_fd_nonblocking(task, pp[0]) != NXT_OK) { return NXT_ERROR; } } if (nbwrite) { if (nxt_fd_nonblocking(task, pp[1]) != NXT_OK) { return NXT_ERROR; } } return NXT_OK; } void nxt_pipe_close(nxt_task_t *task, nxt_fd_t *pp) { nxt_debug(task, "pipe close(%FD:%FD)", pp[0], pp[1]); if (close(pp[0]) != 0) { nxt_alert(task, "pipe close(%FD) failed %E", pp[0], nxt_errno); } if (close(pp[1]) != 0) { nxt_alert(task, "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; }