diff options
Diffstat (limited to '')
-rw-r--r-- | src/system/EPollFD.cxx | 32 | ||||
-rw-r--r-- | src/system/EPollFD.hxx | 80 | ||||
-rw-r--r-- | src/system/EventFD.cxx | 65 | ||||
-rw-r--r-- | src/system/EventFD.hxx | 57 | ||||
-rw-r--r-- | src/system/EventPipe.cxx | 180 | ||||
-rw-r--r-- | src/system/EventPipe.hxx | 57 | ||||
-rw-r--r-- | src/system/FatalError.cxx | 105 | ||||
-rw-r--r-- | src/system/FatalError.hxx | 69 | ||||
-rw-r--r-- | src/system/Resolver.cxx | 146 | ||||
-rw-r--r-- | src/system/Resolver.hxx | 62 | ||||
-rw-r--r-- | src/system/SignalFD.cxx | 58 | ||||
-rw-r--r-- | src/system/SignalFD.hxx | 62 | ||||
-rw-r--r-- | src/system/SocketError.cxx | 46 | ||||
-rw-r--r-- | src/system/SocketError.hxx | 137 | ||||
-rw-r--r-- | src/system/SocketUtil.cxx | 95 | ||||
-rw-r--r-- | src/system/SocketUtil.hxx | 57 | ||||
-rw-r--r-- | src/system/clock.c (renamed from src/clock.c) | 0 | ||||
-rw-r--r-- | src/system/clock.h | 49 | ||||
-rw-r--r-- | src/system/fd_util.c | 353 | ||||
-rw-r--r-- | src/system/fd_util.h | 168 |
20 files changed, 1878 insertions, 0 deletions
diff --git a/src/system/EPollFD.cxx b/src/system/EPollFD.cxx new file mode 100644 index 000000000..5721c0194 --- /dev/null +++ b/src/system/EPollFD.cxx @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#ifdef USE_EPOLL +#include "EPollFD.hxx" +#include "FatalError.hxx" + +EPollFD::EPollFD() + :fd(::epoll_create1(EPOLL_CLOEXEC)) +{ + if (fd < 0) + FatalSystemError("epoll_create1() failed"); +} + +#endif /* USE_EPOLL */ diff --git a/src/system/EPollFD.hxx b/src/system/EPollFD.hxx new file mode 100644 index 000000000..41f7ec377 --- /dev/null +++ b/src/system/EPollFD.hxx @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EPOLL_FD_HXX +#define MPD_EPOLL_FD_HXX + +#include <assert.h> +#include <sys/epoll.h> +#include <unistd.h> + +#include "check.h" + +struct epoll_event; + +/** + * A class that wraps Linux epoll. + * + * Errors in the constructor are fatal. + */ +class EPollFD { + const int fd; + +public: + EPollFD(); + + ~EPollFD() { + assert(fd >= 0); + + ::close(fd); + } + + EPollFD(const EPollFD &other) = delete; + EPollFD &operator=(const EPollFD &other) = delete; + + int Wait(epoll_event *events, int maxevents, int timeout) { + return ::epoll_wait(fd, events, maxevents, timeout); + } + + bool Control(int op, int _fd, epoll_event *event) { + return ::epoll_ctl(fd, op, _fd, event) >= 0; + } + + bool Add(int _fd, uint32_t events, void *ptr) { + epoll_event e; + e.events = events; + e.data.ptr = ptr; + + return Control(EPOLL_CTL_ADD, _fd, &e); + } + + bool Modify(int _fd, uint32_t events, void *ptr) { + epoll_event e; + e.events = events; + e.data.ptr = ptr; + + return Control(EPOLL_CTL_MOD, _fd, &e); + } + + bool Remove(int _fd) { + return Control(EPOLL_CTL_DEL, _fd, nullptr); + } +}; + +#endif diff --git a/src/system/EventFD.cxx b/src/system/EventFD.cxx new file mode 100644 index 000000000..716ff80ae --- /dev/null +++ b/src/system/EventFD.cxx @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#ifdef USE_EVENTFD +#include "EventFD.hxx" +#include "system/fd_util.h" +#include "system/FatalError.hxx" +#include "gcc.h" + +#include <assert.h> +#include <unistd.h> + +#include <sys/eventfd.h> + +EventFD::EventFD() + :fd(eventfd_cloexec_nonblock(0, 0)) +{ + if (fd < 0) + FatalSystemError("eventfd() failed"); +} + +EventFD::~EventFD() +{ + assert(fd >= 0); + + close(fd); +} + +bool +EventFD::Read() +{ + assert(fd >= 0); + + eventfd_t value; + return read(fd, &value, sizeof(value)) == (ssize_t)sizeof(value); +} + +void +EventFD::Write() +{ + assert(fd >= 0); + + static constexpr eventfd_t value = 1; + gcc_unused ssize_t nbytes = + write(fd, &value, sizeof(value)); +} + +#endif /* USE_EVENTFD */ diff --git a/src/system/EventFD.hxx b/src/system/EventFD.hxx new file mode 100644 index 000000000..67a0258ab --- /dev/null +++ b/src/system/EventFD.hxx @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_FD_HXX +#define MPD_EVENT_FD_HXX + +#include "check.h" + +/** + * A class that wraps eventfd(). + * + * Errors in the constructor are fatal. + */ +class EventFD { + int fd; + +public: + EventFD(); + ~EventFD(); + + EventFD(const EventFD &other) = delete; + EventFD &operator=(const EventFD &other) = delete; + + int Get() const { + return fd; + } + + /** + * Checks if Write() was called at least once since the last + * Read() call. + */ + bool Read(); + + /** + * Wakes up the reader. Multiple calls to this function will + * be combined to one wakeup. + */ + void Write(); +}; + +#endif diff --git a/src/system/EventPipe.cxx b/src/system/EventPipe.cxx new file mode 100644 index 000000000..e1a38c503 --- /dev/null +++ b/src/system/EventPipe.cxx @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "EventPipe.hxx" +#include "system/fd_util.h" +#include "system/FatalError.hxx" +#include "gcc.h" + +#include <assert.h> +#include <unistd.h> + +#ifdef WIN32 +#include <ws2tcpip.h> +#include <winsock2.h> +#include <cstring> /* for memset() */ +#endif + +#ifdef WIN32 +static bool PoorSocketPair(int fd[2]); +#endif + +EventPipe::EventPipe() +{ +#ifdef WIN32 + bool success = PoorSocketPair(fds); +#else + bool success = pipe_cloexec_nonblock(fds) >= 0; +#endif + if (!success) + FatalSystemError("pipe() has failed"); +} + +EventPipe::~EventPipe() +{ +#ifdef WIN32 + closesocket(fds[0]); + closesocket(fds[1]); +#else + close(fds[0]); + close(fds[1]); +#endif +} + +bool +EventPipe::Read() +{ + assert(fds[0] >= 0); + assert(fds[1] >= 0); + + char buffer[256]; +#ifdef WIN32 + return recv(fds[0], buffer, sizeof(buffer), 0) > 0; +#else + return read(fds[0], buffer, sizeof(buffer)) > 0; +#endif +} + +void +EventPipe::Write() +{ + assert(fds[0] >= 0); + assert(fds[1] >= 0); + +#ifdef WIN32 + send(fds[1], "", 1, 0); +#else + gcc_unused ssize_t nbytes = write(fds[1], "", 1); +#endif +} + +#ifdef WIN32 + +static void SafeCloseSocket(SOCKET s) +{ + int error = WSAGetLastError(); + closesocket(s); + WSASetLastError(error); +} + +/* Our poor man's socketpair() implementation + * Due to limited protocol/address family support and primitive error handling + * it's better to keep this as a private implementation detail of EventPipe + * rather than wide-available API. + */ +static bool PoorSocketPair(int fd[2]) +{ + assert (fd != nullptr); + + SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_socket == INVALID_SOCKET) + return false; + + sockaddr_in address; + std::memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + int ret = bind(listen_socket, + reinterpret_cast<sockaddr*>(&address), + sizeof(address)); + + if (ret < 0) { + SafeCloseSocket(listen_socket); + return false; + } + + ret = listen(listen_socket, 1); + + if (ret < 0) { + SafeCloseSocket(listen_socket); + return false; + } + + int address_len = sizeof(address); + ret = getsockname(listen_socket, + reinterpret_cast<sockaddr*>(&address), + &address_len); + + if (ret < 0) { + SafeCloseSocket(listen_socket); + return false; + } + + SOCKET socket0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (socket0 == INVALID_SOCKET) { + SafeCloseSocket(listen_socket); + return false; + } + + ret = connect(socket0, + reinterpret_cast<sockaddr*>(&address), + sizeof(address)); + + if (ret < 0) { + SafeCloseSocket(listen_socket); + SafeCloseSocket(socket0); + return false; + } + + SOCKET socket1 = accept(listen_socket, nullptr, nullptr); + if (socket1 == INVALID_SOCKET) { + SafeCloseSocket(listen_socket); + SafeCloseSocket(socket0); + return false; + } + + SafeCloseSocket(listen_socket); + + u_long non_block = 1; + if (ioctlsocket(socket0, FIONBIO, &non_block) < 0 + || ioctlsocket(socket1, FIONBIO, &non_block) < 0) { + SafeCloseSocket(socket0); + SafeCloseSocket(socket1); + return false; + } + + fd[0] = static_cast<int>(socket0); + fd[1] = static_cast<int>(socket1); + + return true; +} + +#endif diff --git a/src/system/EventPipe.hxx b/src/system/EventPipe.hxx new file mode 100644 index 000000000..86a10b0bb --- /dev/null +++ b/src/system/EventPipe.hxx @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_EVENT_PIPE_HXX +#define MPD_EVENT_PIPE_HXX + +#include "check.h" + +/** + * A pipe that can be used to trigger an event to the read side. + * + * Errors in the constructor are fatal. + */ +class EventPipe { + int fds[2]; + +public: + EventPipe(); + ~EventPipe(); + + EventPipe(const EventPipe &other) = delete; + EventPipe &operator=(const EventPipe &other) = delete; + + int Get() const { + return fds[0]; + } + + /** + * Checks if Write() was called at least once since the last + * Read() call. + */ + bool Read(); + + /** + * Wakes up the reader. Multiple calls to this function will + * be combined to one wakeup. + */ + void Write(); +}; + +#endif /* MAIN_NOTIFY_H */ diff --git a/src/system/FatalError.cxx b/src/system/FatalError.cxx new file mode 100644 index 000000000..7c0dc942a --- /dev/null +++ b/src/system/FatalError.cxx @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "FatalError.hxx" +#include "util/Error.hxx" + +#include <glib.h> + +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> + +#ifdef WIN32 +#include <windows.h> +#else +#include <errno.h> +#endif + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "FatalError" + +void +FatalError(const char *msg) +{ + g_critical("%s", msg); + exit(EXIT_FAILURE); +} + +void +FormatFatalError(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + g_logv(G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, fmt, ap); + va_end(ap); + + exit(EXIT_FAILURE); +} + +void +FatalError(const Error &error) +{ + FatalError(error.GetMessage()); +} + +void +FatalError(const char *msg, const Error &error) +{ + FormatFatalError("%s: %s", msg, error.GetMessage()); +} + +void +FatalError(GError *error) +{ + FatalError(error->message); +} + +void +FatalError(const char *msg, GError *error) +{ + FormatFatalError("%s: %s", msg, error->message); +} + +void +FatalSystemError(const char *msg) +{ + const char *system_error; +#ifdef WIN32 + system_error = g_win32_error_message(GetLastError()); +#else + system_error = g_strerror(errno); +#endif + + g_critical("%s: %s", msg, system_error); + exit(EXIT_FAILURE); +} + +void +FormatFatalSystemError(const char *fmt, ...) +{ + char buffer[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, ap); + va_end(ap); + + FatalSystemError(buffer); +} diff --git a/src/system/FatalError.hxx b/src/system/FatalError.hxx new file mode 100644 index 000000000..03baf66c4 --- /dev/null +++ b/src/system/FatalError.hxx @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_FATAL_ERROR_HXX +#define MPD_FATAL_ERROR_HXX + +#include "check.h" +#include "gerror.h" +#include "gcc.h" + +class Error; + +/** + * Log the specified message and abort the process. + */ +gcc_noreturn +void +FatalError(const char *msg); + +gcc_noreturn +void +FormatFatalError(const char *fmt, ...); + +gcc_noreturn +void +FatalError(const Error &error); + +gcc_noreturn +void +FatalError(const char *msg, const Error &error); + +gcc_noreturn +void +FatalError(GError *error); + +gcc_noreturn +void +FatalError(const char *msg, GError *error); + +/** + * Call this after a system call has failed that is not supposed to + * fail. Prints the given message, the system error message (from + * errno or GetLastError()) and abort the process. + */ +gcc_noreturn +void +FatalSystemError(const char *msg); + +gcc_noreturn +void +FormatFatalSystemError(const char *fmt, ...); + +#endif diff --git a/src/system/Resolver.cxx b/src/system/Resolver.cxx new file mode 100644 index 000000000..51a5b1ed7 --- /dev/null +++ b/src/system/Resolver.cxx @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "Resolver.hxx" +#include "util/Error.hxx" +#include "util/Domain.hxx" + +#include <glib.h> + +#ifndef G_OS_WIN32 +#include <sys/socket.h> +#include <netdb.h> +#else /* G_OS_WIN32 */ +#include <ws2tcpip.h> +#include <winsock.h> +#endif /* G_OS_WIN32 */ + +#include <string.h> +#include <stdio.h> + +const Domain resolver_domain("resolver"); + +char * +sockaddr_to_string(const struct sockaddr *sa, size_t length, Error &error) +{ +#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) + const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *)sa; + struct sockaddr_in a4; +#endif + int ret; + char host[NI_MAXHOST], serv[NI_MAXSERV]; + +#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED) + if (sa->sa_family == AF_INET6 && + IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr)) { + /* convert "::ffff:127.0.0.1" to "127.0.0.1" */ + + memset(&a4, 0, sizeof(a4)); + a4.sin_family = AF_INET; + memcpy(&a4.sin_addr, ((const char *)&a6->sin6_addr) + 12, + sizeof(a4.sin_addr)); + a4.sin_port = a6->sin6_port; + + sa = (const struct sockaddr *)&a4; + length = sizeof(a4); + } +#endif + + ret = getnameinfo(sa, length, host, sizeof(host), serv, sizeof(serv), + NI_NUMERICHOST|NI_NUMERICSERV); + if (ret != 0) { + error.Set(resolver_domain, ret, gai_strerror(ret)); + return NULL; + } + +#ifdef HAVE_UN + if (sa->sa_family == AF_UNIX) + /* "serv" contains corrupt information with unix + sockets */ + return g_strdup(host); +#endif + +#ifdef HAVE_IPV6 + if (strchr(host, ':') != NULL) + return g_strconcat("[", host, "]:", serv, NULL); +#endif + + return g_strconcat(host, ":", serv, NULL); +} + +struct addrinfo * +resolve_host_port(const char *host_port, unsigned default_port, + int flags, int socktype, + Error &error) +{ + char *p = g_strdup(host_port); + const char *host = p, *port = NULL; + + if (host_port[0] == '[') { + /* IPv6 needs enclosing square braces, to + differentiate between IP colons and the port + separator */ + + char *q = strchr(p + 1, ']'); + if (q != NULL && q[1] == ':' && q[2] != 0) { + *q = 0; + ++host; + port = q + 2; + } + } + + if (port == NULL) { + /* port is after the colon, but only if it's the only + colon (don't split IPv6 addresses) */ + + char *q = strchr(p, ':'); + if (q != NULL && q[1] != 0 && strchr(q + 1, ':') == NULL) { + *q = 0; + port = q + 1; + } + } + + char buffer[32]; + if (port == NULL && default_port != 0) { + snprintf(buffer, sizeof(buffer), "%u", default_port); + port = buffer; + } + + if ((flags & AI_PASSIVE) != 0 && strcmp(host, "*") == 0) + host = NULL; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = flags; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = socktype; + + struct addrinfo *ai; + int ret = getaddrinfo(host, port, &hints, &ai); + g_free(p); + if (ret != 0) { + error.Format(resolver_domain, ret, + "Failed to look up '%s': %s", + host_port, gai_strerror(ret)); + return NULL; + } + + return ai; +} diff --git a/src/system/Resolver.hxx b/src/system/Resolver.hxx new file mode 100644 index 000000000..38803dcd1 --- /dev/null +++ b/src/system/Resolver.hxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_RESOLVER_HXX +#define MPD_RESOLVER_HXX + +#include "gcc.h" + +#include <stddef.h> + +struct sockaddr; +struct addrinfo; +class Error; + +extern const class Domain resolver_domain; + +/** + * Converts the specified socket address into a string in the form + * "IP:PORT". The return value must be freed with g_free() when you + * don't need it anymore. + * + * @param sa the sockaddr struct + * @param length the length of #sa in bytes + * @param error location to store the error occurring, or NULL to + * ignore errors + */ +gcc_malloc +char * +sockaddr_to_string(const struct sockaddr *sa, size_t length, Error &error); + +/** + * Resolve a specification in the form "host", "host:port", + * "[host]:port". This is a convenience wrapper for getaddrinfo(). + * + * @param default_port a default port number that will be used if none + * is given in the string (if applicable); pass 0 to go without a + * default + * @return an #addrinfo linked list that must be freed with + * freeaddrinfo(), or NULL on error + */ +struct addrinfo * +resolve_host_port(const char *host_port, unsigned default_port, + int flags, int socktype, + Error &error); + +#endif diff --git a/src/system/SignalFD.cxx b/src/system/SignalFD.cxx new file mode 100644 index 000000000..b89775dcd --- /dev/null +++ b/src/system/SignalFD.cxx @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#ifdef USE_SIGNALFD +#include "SignalFD.hxx" +#include "fd_util.h" +#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) + FatalSystemError("signalfd() failed"); +} + +void +SignalFD::Close() +{ + if (fd >= 0) { + ::close(fd); + fd = -1; + } +} + +int +SignalFD::Read() +{ + assert(fd >= 0); + + signalfd_siginfo info; + return read(fd, &info, sizeof(info)) > 0 + ? info.ssi_signo + : -1; +} + +#endif /* USE_SIGNALFD */ diff --git a/src/system/SignalFD.hxx b/src/system/SignalFD.hxx new file mode 100644 index 000000000..7163782d1 --- /dev/null +++ b/src/system/SignalFD.hxx @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SIGNAL_FD_HXX +#define MPD_SIGNAL_FD_HXX + +#include "check.h" + +#include <signal.h> + +/** + * A class that wraps signalfd(). + */ +class SignalFD { + int fd; + +public: + SignalFD():fd(-1) {} + ~SignalFD() { + Close(); + } + + SignalFD(const SignalFD &other) = delete; + SignalFD &operator=(const SignalFD &other) = delete; + + /** + * Create the signalfd or update its mask. + * + * All errors are fatal. + */ + void Create(const sigset_t &mask); + void Close(); + + int Get() const { + return fd; + } + + /** + * Read the next signal from the file descriptor. Returns the + * signal number on success or -1 if there are no more + * signals. + */ + int Read(); +}; + +#endif diff --git a/src/system/SocketError.cxx b/src/system/SocketError.cxx new file mode 100644 index 000000000..315a86e1f --- /dev/null +++ b/src/system/SocketError.cxx @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "SocketError.hxx" +#include "util/Domain.hxx" + +#include <glib.h> + +const Domain socket_domain("socket"); + +#ifdef WIN32 + +SocketErrorMessage::SocketErrorMessage(socket_error_t code) +{ + DWORD nbytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, code, 0, + (LPSTR)msg, sizeof(msg), NULL); + if (nbytes == 0) + strcpy(msg, "Unknown error"); +} + +#else + +SocketErrorMessage::SocketErrorMessage(socket_error_t code) + :msg(g_strerror(code)) {} + +#endif diff --git a/src/system/SocketError.hxx b/src/system/SocketError.hxx new file mode 100644 index 000000000..2b8332715 --- /dev/null +++ b/src/system/SocketError.hxx @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_SOCKET_ERROR_HXX +#define MPD_SOCKET_ERROR_HXX + +#include "gcc.h" +#include "util/Error.hxx" + +#ifdef WIN32 +#include <winsock2.h> +typedef DWORD socket_error_t; +#else +#include <errno.h> +typedef int socket_error_t; +#endif + +/** + * A #Domain for #Error for socket I/O errors. The code is an errno + * value (or WSAGetLastError() on Windows). + */ +extern const class Domain socket_domain; + +gcc_pure +static inline socket_error_t +GetSocketError() +{ +#ifdef WIN32 + return WSAGetLastError(); +#else + return errno; +#endif +} + +gcc_const +static inline bool +IsSocketErrorAgain(socket_error_t code) +{ +#ifdef WIN32 + return code == WSAEINPROGRESS; +#else + return code == EAGAIN; +#endif +} + +gcc_const +static inline bool +IsSocketErrorInterruped(socket_error_t code) +{ +#ifdef WIN32 + return code == WSAEINTR; +#else + return code == EINTR; +#endif +} + +gcc_const +static inline bool +IsSocketErrorClosed(socket_error_t code) +{ +#ifdef WIN32 + return code == WSAECONNRESET; +#else + return code == EPIPE || code == ECONNRESET; +#endif +} + +/** + * Helper class that formats a socket error message into a + * human-readable string. On Windows, a buffer is necessary for this, + * and this class hosts the buffer. + */ +class SocketErrorMessage { +#ifdef WIN32 + char msg[256]; +#else + const char *const msg; +#endif + +public: +#ifdef WIN32 + explicit SocketErrorMessage(socket_error_t code=GetSocketError()); +#else + explicit SocketErrorMessage(socket_error_t code=GetSocketError()); +#endif + + operator const char *() const { + return msg; + } +}; + +static inline void +SetSocketError(Error &error, socket_error_t code) +{ + const SocketErrorMessage msg(code); + error.Set(socket_domain, code, msg); +} + +static inline void +SetSocketError(Error &error) +{ + SetSocketError(error, GetSocketError()); +} + +gcc_const +static inline Error +NewSocketError(socket_error_t code) +{ + Error error; + SetSocketError(error, code); + return error; +} + +gcc_pure +static inline Error +NewSocketError() +{ + return NewSocketError(GetSocketError()); +} + +#endif diff --git a/src/system/SocketUtil.cxx b/src/system/SocketUtil.cxx new file mode 100644 index 000000000..abaedb8e7 --- /dev/null +++ b/src/system/SocketUtil.cxx @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" +#include "SocketUtil.hxx" +#include "SocketError.hxx" +#include "fd_util.h" + +#include <glib.h> + +#include <unistd.h> + +#ifndef G_OS_WIN32 +#include <sys/socket.h> +#else /* G_OS_WIN32 */ +#include <ws2tcpip.h> +#include <winsock.h> +#endif /* G_OS_WIN32 */ + +#ifdef HAVE_IPV6 +#include <string.h> +#endif + +int +socket_bind_listen(int domain, int type, int protocol, + const struct sockaddr *address, size_t address_length, + int backlog, + Error &error) +{ + int fd, ret; + const int reuse = 1; + + fd = socket_cloexec_nonblock(domain, type, protocol); + if (fd < 0) { + SetSocketError(error); + error.AddPrefix("Failed to create socket: "); + return -1; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, + (const char *) &reuse, sizeof(reuse)); + if (ret < 0) { + SetSocketError(error); + error.AddPrefix("setsockopt() failed: "); + close_socket(fd); + return -1; + } + + ret = bind(fd, address, address_length); + if (ret < 0) { + SetSocketError(error); + close_socket(fd); + return -1; + } + + ret = listen(fd, backlog); + if (ret < 0) { + SetSocketError(error); + error.AddPrefix("listen() failed: "); + close_socket(fd); + return -1; + } + +#ifdef HAVE_STRUCT_UCRED + setsockopt(fd, SOL_SOCKET, SO_PASSCRED, + (const char *) &reuse, sizeof(reuse)); +#endif + + return fd; +} + +int +socket_keepalive(int fd) +{ + const int reuse = 1; + + return setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&reuse, sizeof(reuse)); +} diff --git a/src/system/SocketUtil.hxx b/src/system/SocketUtil.hxx new file mode 100644 index 000000000..5e582ec0d --- /dev/null +++ b/src/system/SocketUtil.hxx @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * This library provides easy helper functions for working with + * sockets. + * + */ + +#ifndef MPD_SOCKET_UTIL_HXX +#define MPD_SOCKET_UTIL_HXX + +#include <stddef.h> + +struct sockaddr; +class Error; + +/** + * Creates a socket listening on the specified address. This is a + * shortcut for socket(), bind() and listen(). + * + * @param domain the socket domain, e.g. PF_INET6 + * @param type the socket type, e.g. SOCK_STREAM + * @param protocol the protocol, usually 0 to let the kernel choose + * @param address the address to listen on + * @param address_length the size of #address + * @param backlog the backlog parameter for the listen() system call + * @param error location to store the error occurring, or NULL to + * ignore errors + * @return the socket file descriptor or -1 on error + */ +int +socket_bind_listen(int domain, int type, int protocol, + const struct sockaddr *address, size_t address_length, + int backlog, + Error &error); + +int +socket_keepalive(int fd); + +#endif diff --git a/src/clock.c b/src/system/clock.c index d987aed48..d987aed48 100644 --- a/src/clock.c +++ b/src/system/clock.c diff --git a/src/system/clock.h b/src/system/clock.h new file mode 100644 index 000000000..c98b9a652 --- /dev/null +++ b/src/system/clock.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2003-2012 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_CLOCK_H +#define MPD_CLOCK_H + +#include "gcc.h" + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Returns the value of a monotonic clock in milliseconds. + */ +gcc_pure +unsigned +monotonic_clock_ms(void); + +/** + * Returns the value of a monotonic clock in microseconds. + */ +gcc_pure +uint64_t +monotonic_clock_us(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/system/fd_util.c b/src/system/fd_util.c new file mode 100644 index 000000000..17976a5ee --- /dev/null +++ b/src/system/fd_util.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.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" /* must be first for large file support */ +#include "fd_util.h" + +#if !defined(_GNU_SOURCE) && (defined(HAVE_PIPE2) || defined(HAVE_ACCEPT4)) +#define _GNU_SOURCE +#endif + +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> + +#ifdef WIN32 +#include <ws2tcpip.h> +#include <winsock2.h> +#else +#include <sys/socket.h> +#endif + +#ifdef HAVE_INOTIFY_INIT +#include <sys/inotify.h> +#endif + +#ifdef USE_EVENTFD +#include <sys/eventfd.h> +#endif + +#ifndef WIN32 + +static int +fd_mask_flags(int fd, int and_mask, int xor_mask) +{ + int ret; + + assert(fd >= 0); + + ret = fcntl(fd, F_GETFD, 0); + if (ret < 0) + return ret; + + return fcntl(fd, F_SETFD, (ret & and_mask) ^ xor_mask); +} + +#endif /* !WIN32 */ + +static int +fd_set_cloexec(int fd, bool enable) +{ +#ifndef WIN32 + return fd_mask_flags(fd, ~FD_CLOEXEC, enable ? FD_CLOEXEC : 0); +#else + (void)fd; + (void)enable; + + return 0; +#endif +} + +/** + * Enables non-blocking mode for the specified file descriptor. On + * WIN32, this function only works for sockets. + */ +static int +fd_set_nonblock(int fd) +{ +#ifdef WIN32 + u_long val = 1; + return ioctlsocket(fd, FIONBIO, &val); +#else + int flags; + + assert(fd >= 0); + + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return flags; + + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +#endif +} + +int +dup_cloexec(int oldfd) +{ + int newfd = dup(oldfd); + if (newfd >= 0) + fd_set_nonblock(newfd); + + return newfd; +} + +int +open_cloexec(const char *path_fs, int flags, int mode) +{ + int fd; + +#ifdef O_CLOEXEC + flags |= O_CLOEXEC; +#endif + +#ifdef O_NOCTTY + flags |= O_NOCTTY; +#endif + + fd = open(path_fs, flags, mode); + if (fd >= 0) + fd_set_cloexec(fd, true); + + return fd; +} + +int +pipe_cloexec(int fd[2]) +{ +#ifdef WIN32 + return _pipe(fd, 512, _O_BINARY); +#else + int ret; + +#ifdef HAVE_PIPE2 + ret = pipe2(fd, O_CLOEXEC); + if (ret >= 0 || errno != ENOSYS) + return ret; +#endif + + ret = pipe(fd); + if (ret >= 0) { + fd_set_cloexec(fd[0], true); + fd_set_cloexec(fd[1], true); + } + + return ret; +#endif +} + +int +pipe_cloexec_nonblock(int fd[2]) +{ +#ifdef WIN32 + return _pipe(fd, 512, _O_BINARY); +#else + int ret; + +#ifdef HAVE_PIPE2 + ret = pipe2(fd, O_CLOEXEC|O_NONBLOCK); + if (ret >= 0 || errno != ENOSYS) + return ret; +#endif + + ret = pipe(fd); + if (ret >= 0) { + fd_set_cloexec(fd[0], true); + fd_set_cloexec(fd[1], true); + + fd_set_nonblock(fd[0]); + fd_set_nonblock(fd[1]); + } + + return ret; +#endif +} + +#ifndef WIN32 + +int +socketpair_cloexec(int domain, int type, int protocol, int sv[2]) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socketpair(domain, type | SOCK_CLOEXEC, protocol, sv); + if (ret >= 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret >= 0) { + fd_set_cloexec(sv[0], true); + fd_set_cloexec(sv[1], true); + } + + return ret; +} + +int +socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]) +{ + int ret; + +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + ret = socketpair(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol, + sv); + if (ret >= 0 || errno != EINVAL) + return ret; +#endif + + ret = socketpair(domain, type, protocol, sv); + if (ret >= 0) { + fd_set_cloexec(sv[0], true); + fd_set_nonblock(sv[0]); + fd_set_cloexec(sv[1], true); + fd_set_nonblock(sv[1]); + } + + return ret; +} + +#endif + +int +socket_cloexec_nonblock(int domain, int type, int protocol) +{ + int fd; + +#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK) + fd = socket(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol); + if (fd >= 0 || errno != EINVAL) + return fd; +#endif + + fd = socket(domain, type, protocol); + if (fd >= 0) { + fd_set_cloexec(fd, true); + fd_set_nonblock(fd); + } + + return fd; +} + +int +accept_cloexec_nonblock(int fd, struct sockaddr *address, + size_t *address_length_r) +{ + int ret; + socklen_t address_length = *address_length_r; + +#ifdef HAVE_ACCEPT4 + ret = accept4(fd, address, &address_length, + SOCK_CLOEXEC|SOCK_NONBLOCK); + if (ret >= 0 || errno != ENOSYS) { + if (ret >= 0) + *address_length_r = address_length; + + return ret; + } +#endif + + ret = accept(fd, address, &address_length); + if (ret >= 0) { + fd_set_cloexec(ret, true); + fd_set_nonblock(ret); + *address_length_r = address_length; + } + + return ret; +} + +#ifndef WIN32 + +ssize_t +recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags) +{ +#ifdef MSG_CMSG_CLOEXEC + flags |= MSG_CMSG_CLOEXEC; +#endif + + ssize_t result = recvmsg(sockfd, msg, flags); + if (result >= 0) { + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + while (cmsg != NULL) { + if (cmsg->cmsg_type == SCM_RIGHTS) { + const int *fd_p = (const int *)CMSG_DATA(cmsg); + fd_set_cloexec(*fd_p, true); + } + + cmsg = CMSG_NXTHDR(msg, cmsg); + } + } + + return result; +} + +#endif + +#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) +{ +#ifdef WIN32 + return closesocket(fd); +#else + return close(fd); +#endif +} diff --git a/src/system/fd_util.h b/src/system/fd_util.h new file mode 100644 index 000000000..9003b1616 --- /dev/null +++ b/src/system/fd_util.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2003-2011 The Music Player Daemon Project + * http://www.musicpd.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. + */ + +/* + * This library provides easy helper functions for working with file + * descriptors. It has wrappers for taking advantage of Linux 2.6 + * specific features like O_CLOEXEC. + * + */ + +#ifndef FD_UTIL_H +#define FD_UTIL_H + +#include "check.h" + +#include <stdbool.h> +#include <stddef.h> + +#ifndef WIN32 +#if !defined(_GNU_SOURCE) && (defined(HAVE_PIPE2) || defined(HAVE_ACCEPT4)) +#define _GNU_SOURCE +#endif + +#include <sys/types.h> +#endif + +struct sockaddr; + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Wrapper for dup(), which sets the CLOEXEC flag on the new + * descriptor. + */ +int +dup_cloexec(int oldfd); + +/** + * Wrapper for open(), which sets the CLOEXEC flag (atomically if + * supported by the OS). + */ +int +open_cloexec(const char *path_fs, int flags, int mode); + +/** + * Wrapper for pipe(), which sets the CLOEXEC flag (atomically if + * supported by the OS). + */ +int +pipe_cloexec(int fd[2]); + +/** + * Wrapper for pipe(), which sets the CLOEXEC flag (atomically if + * supported by the OS). + * + * On systems that supports it (everybody except for Windows), it also + * sets the NONBLOCK flag. + */ +int +pipe_cloexec_nonblock(int fd[2]); + +#ifndef WIN32 + +/** + * Wrapper for socketpair(), which sets the CLOEXEC flag (atomically + * if supported by the OS). + */ +int +socketpair_cloexec(int domain, int type, int protocol, int sv[2]); + +/** + * Wrapper for socketpair(), which sets the flags CLOEXEC and NONBLOCK + * (atomically if supported by the OS). + */ +int +socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]); + +#endif + +/** + * Wrapper for socket(), which sets the CLOEXEC and the NONBLOCK flag + * (atomically if supported by the OS). + */ +int +socket_cloexec_nonblock(int domain, int type, int protocol); + +/** + * Wrapper for accept(), which sets the CLOEXEC and the NONBLOCK flags + * (atomically if supported by the OS). + */ +int +accept_cloexec_nonblock(int fd, struct sockaddr *address, + size_t *address_length_r); + + +#ifndef WIN32 + +struct msghdr; + +/** + * Wrapper for recvmsg(), which sets the CLOEXEC flag (atomically if + * supported by the OS). + */ +ssize_t +recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags); + +#endif + +#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. + */ +int +close_socket(int fd); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif |