system/FileDescriptor: new wrapper class for a file descriptor

This commit is contained in:
Max Kellermann 2015-03-03 17:03:21 +01:00
parent 818d729d8b
commit 40a587bbaf
14 changed files with 443 additions and 124 deletions

View File

@ -57,9 +57,9 @@ src_mpd_LDADD = \
libevent.a \
libthread.a \
libnet.a \
$(FS_LIBS) \
libsystem.a \
libutil.a \
$(FS_LIBS) \
$(ICU_LDADD) \
$(SYSTEMD_DAEMON_LIBS) \
$(GLIB_LIBS)
@ -431,6 +431,7 @@ libnet_a_SOURCES = \
libsystem_a_SOURCES = \
src/system/ByteOrder.hxx \
src/system/FatalError.cxx src/system/FatalError.hxx \
src/system/FileDescriptor.cxx src/system/FileDescriptor.hxx \
src/system/fd_util.c src/system/fd_util.h \
src/system/EventPipe.cxx src/system/EventPipe.hxx \
src/system/EventFD.cxx src/system/EventFD.hxx \

View File

@ -21,7 +21,7 @@
#include "InotifySource.hxx"
#include "InotifyDomain.hxx"
#include "util/Error.hxx"
#include "system/fd_util.h"
#include "system/FileDescriptor.hxx"
#include "system/FatalError.hxx"
#include "Log.hxx"
@ -72,8 +72,8 @@ InotifySource::OnSocketReady(gcc_unused unsigned flags)
inline
InotifySource::InotifySource(EventLoop &_loop,
mpd_inotify_callback_t _callback, void *_ctx,
int _fd)
:SocketMonitor(_fd, _loop),
FileDescriptor _fd)
:SocketMonitor(_fd.Get(), _loop),
callback(_callback), callback_ctx(_ctx)
{
ScheduleRead();
@ -85,8 +85,8 @@ InotifySource::Create(EventLoop &loop,
mpd_inotify_callback_t callback, void *callback_ctx,
Error &error)
{
int fd = inotify_init_cloexec();
if (fd < 0) {
FileDescriptor fd;
if (!fd.CreateInotify()) {
error.SetErrno("inotify_init() has failed");
return nullptr;
}

View File

@ -24,6 +24,7 @@
#include "Compiler.h"
class Error;
class FileDescriptor;
typedef void (*mpd_inotify_callback_t)(int wd, unsigned mask,
const char *name, void *ctx);
@ -33,7 +34,8 @@ class InotifySource final : private SocketMonitor {
void *callback_ctx;
InotifySource(EventLoop &_loop,
mpd_inotify_callback_t callback, void *ctx, int fd);
mpd_inotify_callback_t callback, void *ctx,
FileDescriptor fd);
public:
~InotifySource() {

View File

@ -75,17 +75,11 @@ FileReader::Close()
#else
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
FileReader::FileReader(Path _path, Error &error)
:path(_path),
fd(OpenFile(path,
O_RDONLY,
0))
:path(_path)
{
if (fd < 0)
fd.OpenReadOnly(path.c_str());
if (!fd.IsDefined())
error.FormatErrno("Failed to open %s", path.c_str());
}
@ -94,7 +88,7 @@ FileReader::Read(void *data, size_t size, Error &error)
{
assert(IsDefined());
ssize_t nbytes = read(fd, data, size);
ssize_t nbytes = fd.Read(data, size);
if (nbytes < 0) {
error.FormatErrno("Failed to read from %s", path.c_str());
nbytes = 0;
@ -108,7 +102,7 @@ FileReader::Seek(off_t offset, Error &error)
{
assert(IsDefined());
auto result = lseek(fd, offset, SEEK_SET);
auto result = fd.Seek(offset);
const bool success = result >= 0;
if (!success)
error.SetErrno("Failed to seek");
@ -121,8 +115,7 @@ FileReader::Close()
{
assert(IsDefined());
close(fd);
fd = -1;
fd.Close();
}
#endif

View File

@ -25,6 +25,10 @@
#include "fs/AllocatedPath.hxx"
#include "Compiler.h"
#ifndef WIN32
#include "system/FileDescriptor.hxx"
#endif
#include <assert.h>
#ifdef WIN32
@ -39,7 +43,7 @@ class FileReader final : public Reader {
#ifdef WIN32
HANDLE handle;
#else
int fd;
FileDescriptor fd;
#endif
public:
@ -55,7 +59,7 @@ public:
#ifdef WIN32
return handle != INVALID_HANDLE_VALUE;
#else
return fd >= 0;
return fd.IsDefined();
#endif
}

View File

@ -23,10 +23,8 @@
#include "../InputPlugin.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "fs/FileSystem.hxx"
#include "fs/Path.hxx"
#include "system/fd_util.h"
#include "open.h"
#include "system/FileDescriptor.hxx"
#include <sys/stat.h>
#include <unistd.h>
@ -35,10 +33,10 @@
static constexpr Domain file_domain("file");
class FileInputStream final : public InputStream {
const int fd;
FileDescriptor fd;
public:
FileInputStream(const char *path, int _fd, off_t _size,
FileInputStream(const char *path, FileDescriptor _fd, off_t _size,
Mutex &_mutex, Cond &_cond)
:InputStream(path, _mutex, _cond),
fd(_fd) {
@ -48,7 +46,7 @@ public:
}
~FileInputStream() {
close(fd);
fd.Close();
}
/* virtual methods from InputStream */
@ -66,29 +64,29 @@ OpenFileInputStream(Path path,
Mutex &mutex, Cond &cond,
Error &error)
{
const int fd = OpenFile(path, O_RDONLY|O_BINARY, 0);
if (fd < 0) {
FileDescriptor fd;
if (!fd.OpenReadOnly(path.c_str())) {
error.FormatErrno("Failed to open \"%s\"",
path.c_str());
return nullptr;
}
struct stat st;
if (fstat(fd, &st) < 0) {
if (fstat(fd.Get(), &st) < 0) {
error.FormatErrno("Failed to stat \"%s\"", path.c_str());
close(fd);
fd.Close();
return nullptr;
}
if (!S_ISREG(st.st_mode)) {
error.Format(file_domain, "Not a regular file: %s",
path.c_str());
close(fd);
fd.Close();
return nullptr;
}
#ifdef POSIX_FADV_SEQUENTIAL
posix_fadvise(fd, (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
posix_fadvise(fd.Get(), (off_t)0, st.st_size, POSIX_FADV_SEQUENTIAL);
#endif
return new FileInputStream(path.c_str(), fd, st.st_size, mutex, cond);
@ -107,7 +105,7 @@ input_file_open(gcc_unused const char *filename,
bool
FileInputStream::Seek(offset_type new_offset, Error &error)
{
auto result = lseek(fd, (off_t)new_offset, SEEK_SET);
auto result = fd.Seek((off_t)new_offset);
if (result < 0) {
error.SetErrno("Failed to seek");
return false;
@ -120,7 +118,7 @@ FileInputStream::Seek(offset_type new_offset, Error &error)
size_t
FileInputStream::Read(void *ptr, size_t read_size, Error &error)
{
ssize_t nbytes = read(fd, ptr, read_size);
ssize_t nbytes = fd.Read(ptr, read_size);
if (nbytes < 0) {
error.SetErrno("Failed to read");
return 0;

View File

@ -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 <assert.h>
#include <unistd.h>
#include <sys/eventfd.h>
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 */

View File

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

View File

@ -0,0 +1,219 @@
/*
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
*
* 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 <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_POSIX
#include <poll.h>
#endif
#ifdef USE_EVENTFD
#include <sys/eventfd.h>
#endif
#ifdef USE_SIGNALFD
#include <sys/signalfd.h>
#endif
#ifdef HAVE_INOTIFY_INIT
#include <sys/inotify.h>
#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

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2012-2015 Max Kellermann <max@duempel.org>
*
* 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 <assert.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef USE_SIGNALFD
#include <signal.h>
#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

View File

@ -23,33 +23,29 @@
#include "FatalError.hxx"
#include <assert.h>
#include <unistd.h>
#include <sys/signalfd.h>
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;
}

View File

@ -21,6 +21,7 @@
#define MPD_SIGNAL_FD_HXX
#include "check.h"
#include "FileDescriptor.hxx"
#include <signal.h>
@ -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();
}
/**

View File

@ -41,10 +41,6 @@
#include <sys/socket.h>
#endif
#ifdef HAVE_INOTIFY_INIT
#include <sys/inotify.h>
#endif
#ifdef USE_EVENTFD
#include <sys/eventfd.h>
#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)
{

View File

@ -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.
*/