summaryrefslogtreecommitdiff
path: root/src/Listen.cxx
blob: 14e19409972c47827d470af56b8ad9b6efa55677 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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
/*
 * Copyright 2003-2021 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 "Listen.hxx"
#include "Log.hxx"
#include "client/Listener.hxx"
#include "config/Param.hxx"
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "config/Net.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "net/SocketUtil.hxx"
#include "system/Error.hxx"
#include "util/RuntimeError.hxx"
#include "fs/AllocatedPath.hxx"
#include "fs/XDG.hxx"

#include <sys/stat.h>

#ifdef ENABLE_SYSTEMD_DAEMON
#include <systemd/sd-daemon.h>
#endif

#define DEFAULT_PORT	6600

int listen_port;

#ifdef ENABLE_SYSTEMD_DAEMON

static bool
listen_systemd_activation(ClientListener &listener)
{
	int n = sd_listen_fds(true);
	if (n <= 0) {
		if (n < 0)
			throw MakeErrno(-n, "sd_listen_fds() failed");
		return false;
	}

	for (int i = SD_LISTEN_FDS_START, end = SD_LISTEN_FDS_START + n;
	     i != end; ++i)
		listener.AddFD(UniqueSocketDescriptor(i));

	return true;
}

#endif

/**
 * Listen on "$XDG_RUNTIME_DIR/mpd/socket" (if applicable).
 *
 * @return true if a listener socket was added
 */
static bool
ListenXdgRuntimeDir(ClientListener &listener) noexcept
{
#if defined(USE_XDG) && defined(HAVE_UN)
	if (geteuid() == 0)
		/* this MPD instance is a system-wide daemon; don't
		   use $XDG_RUNTIME_DIR */
		return false;

	Path xdg_runtime_dir = Path::FromFS(getenv("XDG_RUNTIME_DIR"));
	if (xdg_runtime_dir.IsNull())
		return false;

	const auto mpd_runtime_dir = xdg_runtime_dir / Path::FromFS("mpd");
	mkdir(mpd_runtime_dir.c_str(), 0700);

	const auto socket_path = mpd_runtime_dir / Path::FromFS("socket");
	unlink(socket_path.c_str());

	AllocatedSocketAddress address;
	address.SetLocal(socket_path.c_str());

	try {
		auto fd = socket_bind_listen(AF_LOCAL, SOCK_STREAM, 0,
					     address, 5);
		chmod(socket_path.c_str(), 0600);
		listener.AddFD(std::move(fd), std::move(address));
		return true;
	} catch (...) {
		FormatError(std::current_exception(),
			    "Failed to listen on '%s' (not fatal)",
			    socket_path.c_str());
		return false;
	}
#else
	(void)listener;
	return false;
#endif
}

void
listen_global_init(const ConfigData &config, ClientListener &listener)
{
	int port = config.GetPositive(ConfigOption::PORT, DEFAULT_PORT);

#ifdef ENABLE_SYSTEMD_DAEMON
	if (listen_systemd_activation(listener))
		return;
#endif

	for (const auto &param : config.GetParamList(ConfigOption::BIND_TO_ADDRESS)) {
		try {
			ServerSocketAddGeneric(listener, param.value.c_str(),
					       port);
		} catch (...) {
			std::throw_with_nested(FormatRuntimeError("Failed to listen on %s (line %i)",
								  param.value.c_str(),
								  param.line));
		}
	}

	bool have_xdg_runtime_listener = false;

	if (listener.IsEmpty()) {
		/* no "bind_to_address" configured, bind the
		   configured port on all interfaces */

		have_xdg_runtime_listener = ListenXdgRuntimeDir(listener);

		try {
			listener.AddPort(port);
		} catch (...) {
			std::throw_with_nested(FormatRuntimeError("Failed to listen on *:%d: ", port));
		}
	}

	try {
		listener.Open();
	} catch (...) {
		if (have_xdg_runtime_listener)
			LogError(std::current_exception(),
				 "Default TCP listener setup failed, but this is okay because we have a $XDG_RUNTIME_DIR listener");
		else
			throw;
	}

	listen_port = port;
}