Listen.cxx 3.96 KB
Newer Older
1
/*
Max Kellermann's avatar
Max Kellermann committed
2
 * Copyright (C) 2003-2014 The Music Player Daemon Project
3
 * http://www.musicpd.org
Warren Dukes's avatar
Warren Dukes committed
4 5 6 7 8 9 10 11 12 13
 *
 * 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.
14 15 16 17
 *
 * 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.
Warren Dukes's avatar
Warren Dukes committed
18 19
 */

20
#include "config.h"
Max Kellermann's avatar
Max Kellermann committed
21 22
#include "Listen.hxx"
#include "Main.hxx"
23
#include "Instance.hxx"
24
#include "client/Client.hxx"
25 26 27
#include "config/ConfigData.hxx"
#include "config/ConfigGlobal.hxx"
#include "config/ConfigOption.hxx"
28
#include "event/ServerSocket.hxx"
29
#include "util/Error.hxx"
30
#include "util/Domain.hxx"
31
#include "fs/AllocatedPath.hxx"
32
#include "Log.hxx"
Max Kellermann's avatar
Max Kellermann committed
33 34

#include <string.h>
35
#include <assert.h>
36

37 38 39 40
#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
#endif

41
static constexpr Domain listen_domain("listen");
42

43
#define DEFAULT_PORT	6600
Warren Dukes's avatar
Warren Dukes committed
44

45 46
class ClientListener final : public ServerSocket {
public:
47
	ClientListener(EventLoop &_loop):ServerSocket(_loop) {}
48 49 50 51

private:
	virtual void OnAccept(int fd, const sockaddr &address,
			      size_t address_length, int uid) {
52
		client_new(GetEventLoop(), *instance->partition,
53 54 55
			   fd, &address, address_length, uid);
	}
};
Warren Dukes's avatar
Warren Dukes committed
56

57 58
static ClientListener *listen_socket;
int listen_port;
59

60 61 62
static bool
listen_add_config_param(unsigned int port,
			const struct config_param *param,
63
			Error &error_r)
64
{
65
	assert(param != nullptr);
66

67
	if (0 == strcmp(param->value.c_str(), "any")) {
68
		return listen_socket->AddPort(port, error_r);
69
	} else if (param->value[0] == '/' || param->value[0] == '~') {
70 71 72
		auto path = config_parse_path(param, error_r);
		return !path.IsNull() &&
			listen_socket->AddPath(std::move(path), error_r);
Avuton Olrich's avatar
Avuton Olrich committed
73
	} else {
74 75
		return listen_socket->AddHost(param->value.c_str(), port,
					      error_r);
Warren Dukes's avatar
Warren Dukes committed
76 77 78
	}
}

79
static bool
80
listen_systemd_activation(Error &error_r)
81 82 83 84 85
{
#ifdef ENABLE_SYSTEMD_DAEMON
	int n = sd_listen_fds(true);
	if (n <= 0) {
		if (n < 0)
86 87
			FormatErrno(listen_domain, -n,
				    "sd_listen_fds() failed");
88 89 90 91 92
		return false;
	}

	for (int i = SD_LISTEN_FDS_START, end = SD_LISTEN_FDS_START + n;
	     i != end; ++i)
93
		if (!listen_socket->AddFD(i, error_r))
94 95 96 97 98 99 100 101 102
			return false;

	return true;
#else
	(void)error_r;
	return false;
#endif
}

103
bool
104
listen_global_init(EventLoop &loop, Error &error)
Avuton Olrich's avatar
Avuton Olrich committed
105
{
106
	int port = config_get_positive(CONF_PORT, DEFAULT_PORT);
107
	const struct config_param *param =
108
		config_get_next_param(CONF_BIND_TO_ADDRESS, nullptr);
109
	bool success;
110

111
	listen_socket = new ClientListener(loop);
112

113
	if (listen_systemd_activation(error))
114 115
		return true;

116
	if (error.IsDefined())
117 118
		return false;

119
	if (param != nullptr) {
120 121 122 123
		/* "bind_to_address" is configured, create listeners
		   for all values */

		do {
124
			if (!listen_add_config_param(port, param, error)) {
125
				delete listen_socket;
126
				error.FormatPrefix("Failed to listen on %s (line %i): ",
127 128
						   param->value.c_str(),
						   param->line);
129 130
				return false;
			}
131 132 133

			param = config_get_next_param(CONF_BIND_TO_ADDRESS,
						      param);
134
		} while (param != nullptr);
135 136 137 138
	} else {
		/* no "bind_to_address" configured, bind the
		   configured port on all interfaces */

139
		success = listen_socket->AddPort(port, error);
140
		if (!success) {
141
			delete listen_socket;
142
			error.FormatPrefix("Failed to listen on *:%d: ", port);
143 144
			return false;
		}
145 146
	}

147
	if (!listen_socket->Open(error)) {
148
		delete listen_socket;
149
		return false;
150
	}
151

Max Kellermann's avatar
Max Kellermann committed
152
	listen_port = port;
153
	return true;
154 155
}

Max Kellermann's avatar
Max Kellermann committed
156
void listen_global_finish(void)
Avuton Olrich's avatar
Avuton Olrich committed
157
{
158
	LogDebug(listen_domain, "listen_global_finish called");
159

160
	assert(listen_socket != nullptr);
161

162
	delete listen_socket;
Warren Dukes's avatar
Warren Dukes committed
163
}