EventPipe.cxx 3.83 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * 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"
21
#include "EventPipe.hxx"
22
#include "system/fd_util.h"
23
#include "system/FatalError.hxx"
24
#include "Compiler.h"
25

26
#include <assert.h>
27 28
#include <unistd.h>

29 30 31 32 33 34 35 36 37 38
#ifdef WIN32
#include <ws2tcpip.h>
#include <winsock2.h>
#include <cstring> /* for memset() */
#endif

#ifdef WIN32
static bool PoorSocketPair(int fd[2]);
#endif

39
EventPipe::EventPipe()
40
{
41
#ifdef WIN32
42
	bool success = PoorSocketPair(fds);
43
#else
44
	bool success = pipe_cloexec_nonblock(fds) >= 0;
45
#endif
46 47
	if (!success)
		FatalSystemError("pipe() has failed");
48 49
}

50
EventPipe::~EventPipe()
51
{
52 53 54 55
#ifdef WIN32
	closesocket(fds[0]);
	closesocket(fds[1]);
#else
56
	close(fds[0]);
57 58
	close(fds[1]);
#endif
59 60 61
}

bool
62
EventPipe::Read()
63 64
{
	assert(fds[0] >= 0);
65
	assert(fds[1] >= 0);
66

67
	char buffer[256];
68
#ifdef WIN32
69 70
	return recv(fds[0], buffer, sizeof(buffer), 0) > 0;
#else
71
	return read(fds[0], buffer, sizeof(buffer)) > 0;
72
#endif
73 74 75
}

void
76
EventPipe::Write()
77 78
{
	assert(fds[0] >= 0);
79 80
	assert(fds[1] >= 0);

81
#ifdef WIN32
82 83
	send(fds[1], "", 1, 0);
#else
84
	gcc_unused ssize_t nbytes = write(fds[1], "", 1);
85 86 87 88 89 90 91 92 93 94 95 96 97 98
#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
99
 * it's better to keep this as a private implementation detail of EventPipe
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
 * 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));
118

119 120 121 122 123 124
	if (ret < 0) {
		SafeCloseSocket(listen_socket);
		return false;
	}

	ret = listen(listen_socket, 1);
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	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;
178
}
179 180

#endif