From 40a587bbaff38bb0b60313c24b94e2ecd53b181c Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Tue, 3 Mar 2015 17:03:21 +0100 Subject: system/FileDescriptor: new wrapper class for a file descriptor --- src/system/EventFD.cxx | 21 +--- src/system/EventFD.hxx | 9 +- src/system/FileDescriptor.cxx | 219 ++++++++++++++++++++++++++++++++++++++++++ src/system/FileDescriptor.hxx | 171 +++++++++++++++++++++++++++++++++ src/system/SignalFD.cxx | 14 +-- src/system/SignalFD.hxx | 5 +- src/system/fd_util.c | 36 ------- src/system/fd_util.h | 22 ----- 8 files changed, 409 insertions(+), 88 deletions(-) create mode 100644 src/system/FileDescriptor.cxx create mode 100644 src/system/FileDescriptor.hxx (limited to 'src/system') diff --git a/src/system/EventFD.cxx b/src/system/EventFD.cxx index 2c1cef040..016dd372c 100644 --- a/src/system/EventFD.cxx +++ b/src/system/EventFD.cxx @@ -20,46 +20,35 @@ #include "config.h" #ifdef USE_EVENTFD #include "EventFD.hxx" -#include "system/fd_util.h" #include "system/FatalError.hxx" #include "Compiler.h" #include -#include - #include EventFD::EventFD() - :fd(eventfd_cloexec_nonblock(0, 0)) { - if (fd < 0) + if (!fd.CreateEventFD(0)) FatalSystemError("eventfd() failed"); } -EventFD::~EventFD() -{ - assert(fd >= 0); - - close(fd); -} - bool EventFD::Read() { - assert(fd >= 0); + assert(fd.IsDefined()); eventfd_t value; - return read(fd, &value, sizeof(value)) == (ssize_t)sizeof(value); + return fd.Read(&value, sizeof(value)) == (ssize_t)sizeof(value); } void EventFD::Write() { - assert(fd >= 0); + assert(fd.IsDefined()); static constexpr eventfd_t value = 1; gcc_unused ssize_t nbytes = - write(fd, &value, sizeof(value)); + fd.Write(&value, sizeof(value)); } #endif /* USE_EVENTFD */ diff --git a/src/system/EventFD.hxx b/src/system/EventFD.hxx index 654547078..616877f4a 100644 --- a/src/system/EventFD.hxx +++ b/src/system/EventFD.hxx @@ -21,6 +21,7 @@ #define MPD_EVENT_FD_HXX #include "check.h" +#include "FileDescriptor.hxx" /** * A class that wraps eventfd(). @@ -28,17 +29,19 @@ * Errors in the constructor are fatal. */ class EventFD { - int fd; + FileDescriptor fd; public: EventFD(); - ~EventFD(); + ~EventFD() { + fd.Close(); + } EventFD(const EventFD &other) = delete; EventFD &operator=(const EventFD &other) = delete; int Get() const { - return fd; + return fd.Get(); } /** diff --git a/src/system/FileDescriptor.cxx b/src/system/FileDescriptor.cxx new file mode 100644 index 000000000..946493329 --- /dev/null +++ b/src/system/FileDescriptor.cxx @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "FileDescriptor.hxx" + +#include +#include + +#ifdef HAVE_POSIX +#include +#endif + +#ifdef USE_EVENTFD +#include +#endif + +#ifdef USE_SIGNALFD +#include +#endif + +#ifdef HAVE_INOTIFY_INIT +#include +#endif + +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif + +bool +FileDescriptor::Open(const char *pathname, int flags) +{ + assert(!IsDefined()); + + fd = ::open(pathname, flags); + return IsDefined(); +} + +bool +FileDescriptor::OpenReadOnly(const char *pathname) +{ + return Open(pathname, O_RDONLY | O_NOCTTY | O_CLOEXEC); +} + +#ifdef HAVE_POSIX + +bool +FileDescriptor::OpenNonBlocking(const char *pathname) +{ + return Open(pathname, O_RDWR | O_NOCTTY | O_CLOEXEC | O_NONBLOCK); +} + +bool +FileDescriptor::CreatePipe(FileDescriptor &r, FileDescriptor &w) +{ + int fds[2]; + +#ifdef HAVE_PIPE2 + const int flags = O_CLOEXEC; + const int result = pipe2(fds, flags); +#else + const int result = pipe(fds); +#endif + + if (result < 0) + return false; + + r = FileDescriptor(fds[0]); + w = FileDescriptor(fds[1]); + return true; +} + +void +FileDescriptor::SetNonBlocking() +{ + assert(IsDefined()); + + int flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +void +FileDescriptor::SetBlocking() +{ + assert(IsDefined()); + + int flags = fcntl(fd, F_GETFL); + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +} + +#endif + +#ifdef USE_EVENTFD + +bool +FileDescriptor::CreateEventFD(unsigned initval) +{ + assert(!IsDefined()); + + fd = ::eventfd(initval, EFD_NONBLOCK|EFD_CLOEXEC); + return fd >= 0; +} + +#endif + +#ifdef USE_SIGNALFD + +bool +FileDescriptor::CreateSignalFD(const sigset_t *mask) +{ + int new_fd = ::signalfd(fd, mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (new_fd < 0) + return false; + + fd = new_fd; + return true; +} + +#endif + +#ifdef HAVE_INOTIFY_INIT + +bool +FileDescriptor::CreateInotify() +{ +#ifdef HAVE_INOTIFY_INIT1 + int new_fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK); +#else + int new_fd = inotify_init(); +#endif + if (new_fd < 0) + return false; + +#ifndef HAVE_INOTIFY_INIT1 + SetNonBlocking(); +#endif + + fd = new_fd; + return true; +} + +#endif + +bool +FileDescriptor::Rewind() +{ + assert(IsDefined()); + + return lseek(fd, 0, SEEK_SET) == 0; +} + +off_t +FileDescriptor::GetSize() const +{ + struct stat st; + return ::fstat(fd, &st) >= 0 + ? (long)st.st_size + : -1; +} + +#ifdef HAVE_POSIX + +int +FileDescriptor::Poll(short events, int timeout) const +{ + assert(IsDefined()); + + struct pollfd pfd; + pfd.fd = fd; + pfd.events = events; + int result = poll(&pfd, 1, timeout); + return result > 0 + ? pfd.revents + : result; +} + +int +FileDescriptor::WaitReadable(int timeout) const +{ + return Poll(POLLIN, timeout); +} + +int +FileDescriptor::WaitWritable(int timeout) const +{ + return Poll(POLLOUT, timeout); +} + +#endif diff --git a/src/system/FileDescriptor.hxx b/src/system/FileDescriptor.hxx new file mode 100644 index 000000000..8db0dffdd --- /dev/null +++ b/src/system/FileDescriptor.hxx @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2012-2015 Max Kellermann + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FILE_DESCRIPTOR_HXX +#define FILE_DESCRIPTOR_HXX + +#include "check.h" +#include "Compiler.h" + +#include +#include +#include + +#ifdef USE_SIGNALFD +#include +#endif + +/** + * An OO wrapper for a UNIX file descriptor. + * + * This class is unmanaged and trivial. + */ +class FileDescriptor { +protected: + int fd; + +public: + FileDescriptor() = default; + explicit constexpr FileDescriptor(int _fd):fd(_fd) {} + + constexpr bool operator==(FileDescriptor other) const { + return fd == other.fd; + } + + constexpr bool IsDefined() const { + return fd >= 0; + } + + /** + * Returns the file descriptor. This may only be called if + * IsDefined() returns true. + */ + constexpr int Get() const { + return fd; + } + + void Set(int _fd) { + fd = _fd; + } + + int Steal() { + assert(IsDefined()); + + int _fd = fd; + fd = -1; + return _fd; + } + + void SetUndefined() { + fd = -1; + } + + static constexpr FileDescriptor Undefined() { + return FileDescriptor(-1); + } + + bool Open(const char *pathname, int flags); + bool OpenReadOnly(const char *pathname); + +#ifdef HAVE_POSIX + bool OpenNonBlocking(const char *pathname); + + static bool CreatePipe(FileDescriptor &r, FileDescriptor &w); + + /** + * Enable non-blocking mode on this file descriptor. + */ + void SetNonBlocking(); + + /** + * Enable blocking mode on this file descriptor. + */ + void SetBlocking(); + + /** + * Duplicate the file descriptor onto the given file descriptor. + */ + bool Duplicate(int new_fd) const { + return ::dup2(Get(), new_fd) == 0; + } +#endif + +#ifdef USE_EVENTFD + bool CreateEventFD(unsigned initval=0); +#endif + +#ifdef USE_SIGNALFD + bool CreateSignalFD(const sigset_t *mask); +#endif + +#ifdef HAVE_INOTIFY_INIT + bool CreateInotify(); +#endif + + /** + * Close the file descriptor. It is legal to call it on an + * "undefined" object. After this call, IsDefined() is guaranteed + * to return false, and this object may be reused. + */ + void Close() { + ::close(Steal()); + } + + /** + * Rewind the pointer to the beginning of the file. + */ + bool Rewind(); + + off_t Seek(off_t offset) { + return lseek(Get(), offset, SEEK_SET); + } + + /** + * Returns the size of the file in bytes, or -1 on error. + */ + gcc_pure + off_t GetSize() const; + + ssize_t Read(void *buffer, size_t length) { + return ::read(fd, buffer, length); + } + + ssize_t Write(const void *buffer, size_t length) { + return ::write(fd, buffer, length); + } + +#ifdef HAVE_POSIX + int Poll(short events, int timeout) const; + + int WaitReadable(int timeout) const; + int WaitWritable(int timeout) const; +#endif +}; + +#endif diff --git a/src/system/SignalFD.cxx b/src/system/SignalFD.cxx index 92d2eec01..14193c893 100644 --- a/src/system/SignalFD.cxx +++ b/src/system/SignalFD.cxx @@ -23,33 +23,29 @@ #include "FatalError.hxx" #include -#include #include void SignalFD::Create(const sigset_t &mask) { - fd = ::signalfd(fd, &mask, SFD_NONBLOCK|SFD_CLOEXEC); - if (fd < 0) + if (!fd.CreateSignalFD(&mask)) FatalSystemError("signalfd() failed"); } void SignalFD::Close() { - if (fd >= 0) { - ::close(fd); - fd = -1; - } + if (fd.IsDefined()) + fd.Close(); } int SignalFD::Read() { - assert(fd >= 0); + assert(fd.IsDefined()); signalfd_siginfo info; - return read(fd, &info, sizeof(info)) > 0 + return fd.Read(&info, sizeof(info)) > 0 ? info.ssi_signo : -1; } diff --git a/src/system/SignalFD.hxx b/src/system/SignalFD.hxx index d376c6b59..dae150fea 100644 --- a/src/system/SignalFD.hxx +++ b/src/system/SignalFD.hxx @@ -21,6 +21,7 @@ #define MPD_SIGNAL_FD_HXX #include "check.h" +#include "FileDescriptor.hxx" #include @@ -28,7 +29,7 @@ * A class that wraps signalfd(). */ class SignalFD { - int fd; + FileDescriptor fd; public: SignalFD():fd(-1) {} @@ -48,7 +49,7 @@ public: void Close(); int Get() const { - return fd; + return fd.Get(); } /** diff --git a/src/system/fd_util.c b/src/system/fd_util.c index 440d5b142..5763ede90 100644 --- a/src/system/fd_util.c +++ b/src/system/fd_util.c @@ -41,10 +41,6 @@ #include #endif -#ifdef HAVE_INOTIFY_INIT -#include -#endif - #ifdef USE_EVENTFD #include #endif @@ -200,38 +196,6 @@ accept_cloexec_nonblock(int fd, struct sockaddr *address, return ret; } -#ifdef HAVE_INOTIFY_INIT - -int -inotify_init_cloexec(void) -{ - int fd; - -#ifdef HAVE_INOTIFY_INIT1 - fd = inotify_init1(IN_CLOEXEC); - if (fd >= 0 || errno != ENOSYS) - return fd; -#endif - - fd = inotify_init(); - if (fd >= 0) - fd_set_cloexec(fd, true); - - return fd; -} - -#endif - -#ifdef USE_EVENTFD - -int -eventfd_cloexec_nonblock(unsigned initval, int flags) -{ - return eventfd(initval, flags | EFD_CLOEXEC | EFD_NONBLOCK); -} - -#endif - int close_socket(int fd) { diff --git a/src/system/fd_util.h b/src/system/fd_util.h index 02e959a8e..172b1ade3 100644 --- a/src/system/fd_util.h +++ b/src/system/fd_util.h @@ -91,28 +91,6 @@ int accept_cloexec_nonblock(int fd, struct sockaddr *address, size_t *address_length_r); -#ifdef HAVE_INOTIFY_INIT - -/** - * Wrapper for inotify_init(), which sets the CLOEXEC flag (atomically - * if supported by the OS). - */ -int -inotify_init_cloexec(void); - -#endif - -#ifdef USE_EVENTFD - -/** - * Wrapper for eventfd() which sets the flags CLOEXEC and NONBLOCK - * flag (atomically if supported by the OS). - */ -int -eventfd_cloexec_nonblock(unsigned initval, int flags); - -#endif - /** * Portable wrapper for close(); use closesocket() on WIN32/WinSock. */ -- cgit v1.2.3