Move --user command line setting into config file

This commit is contained in:
badaix 2020-06-07 14:21:58 +02:00
parent 446b22ac85
commit e148d74772
10 changed files with 85 additions and 83 deletions

View file

@ -767,8 +767,10 @@ inline void Value<T>::update_reference()
{
if (this->assign_to_)
{
if (this->is_set() || default_)
*this->assign_to_ = value();
if (!this->is_set() && default_)
*this->assign_to_ = *default_;
else if (this->is_set())
*this->assign_to_ = values_.back();
}
}

View file

@ -26,7 +26,7 @@ SCRIPTNAME=/etc/init.d/$NAME
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
SNAPCLIENT_OPTS="--daemon $SNAPCLIENT_OPTS"
SNAPCLIENT_OPTS="--daemon --user $USERNAME:$USERNAME $SNAPCLIENT_OPTS"
if [ "$START_SNAPCLIENT" != "true" ] ; then
exit 0

View file

@ -32,8 +32,8 @@ using namespace std;
using json = nlohmann::json;
ControlServer::ControlServer(boost::asio::io_context& io_context, const ServerSettings::TcpSettings& tcp_settings,
const ServerSettings::HttpSettings& http_settings, ControlMessageReceiver* controlMessageReceiver)
ControlServer::ControlServer(boost::asio::io_context& io_context, const ServerSettings::Tcp& tcp_settings, const ServerSettings::Http& http_settings,
ControlMessageReceiver* controlMessageReceiver)
: io_context_(io_context), tcp_settings_(tcp_settings), http_settings_(http_settings), controlMessageReceiver_(controlMessageReceiver)
{
}

View file

@ -43,14 +43,14 @@ using acceptor_ptr = std::unique_ptr<tcp::acceptor>;
class ControlServer : public ControlMessageReceiver
{
public:
ControlServer(boost::asio::io_context& io_context, const ServerSettings::TcpSettings& tcp_settings, const ServerSettings::HttpSettings& http_settings,
ControlServer(boost::asio::io_context& io_context, const ServerSettings::Tcp& tcp_settings, const ServerSettings::Http& http_settings,
ControlMessageReceiver* controlMessageReceiver = nullptr);
virtual ~ControlServer();
void start();
void stop();
/// Send a message to all connceted clients
/// Send a message to all connected clients
void send(const std::string& message, const ControlSession* excludeSession = nullptr);
/// Clients call this when they receive a message. Implementation of MessageReceiver::onMessageReceived
@ -70,8 +70,8 @@ private:
std::vector<acceptor_ptr> acceptor_http_;
boost::asio::io_context& io_context_;
ServerSettings::TcpSettings tcp_settings_;
ServerSettings::HttpSettings http_settings_;
ServerSettings::Tcp tcp_settings_;
ServerSettings::Http http_settings_;
ControlMessageReceiver* controlMessageReceiver_;
};

View file

@ -99,7 +99,7 @@ std::string path_cat(boost::beast::string_view base, boost::beast::string_view p
} // namespace
ControlSessionHttp::ControlSessionHttp(ControlMessageReceiver* receiver, boost::asio::io_context& ioc, tcp::socket&& socket,
const ServerSettings::HttpSettings& settings)
const ServerSettings::Http& settings)
: ControlSession(receiver), socket_(std::move(socket)), settings_(settings), strand_(ioc)
{
LOG(DEBUG) << "ControlSessionHttp\n";

View file

@ -40,7 +40,7 @@ class ControlSessionHttp : public ControlSession, public std::enable_shared_from
{
public:
/// ctor. Received message from the client are passed to MessageReceiver
ControlSessionHttp(ControlMessageReceiver* receiver, boost::asio::io_context& ioc, tcp::socket&& socket, const ServerSettings::HttpSettings& settings);
ControlSessionHttp(ControlMessageReceiver* receiver, boost::asio::io_context& ioc, tcp::socket&& socket, const ServerSettings::Http& settings);
~ControlSessionHttp() override;
void start() override;
void stop() override;
@ -74,7 +74,7 @@ protected:
protected:
tcp::socket socket_;
beast::flat_buffer buffer_;
ServerSettings::HttpSettings settings_;
ServerSettings::Http settings_;
boost::asio::io_context::strand strand_;
std::deque<std::string> messages_;
};

View file

@ -32,6 +32,11 @@
# the pid file when running as daemon
#pidfile = /var/run/snapserver/pid
# the user to run as when daemonized
#user = snapserver
# the group to run as when daemonized
#group = snapserver
# directory where persistent data is stored (server.json)
# if empty, data dir will be
# - "/var/lib/snapserver/" when running as daemon

View file

@ -24,7 +24,16 @@
struct ServerSettings
{
struct HttpSettings
struct Server
{
int threads{-1};
std::string pid_file{"/var/run/snapserver/pid"};
std::string user{"snapserver"};
std::string group{""};
std::string data_dir{""};
};
struct Http
{
bool enabled{true};
size_t port{1780};
@ -32,14 +41,14 @@ struct ServerSettings
std::string doc_root{""};
};
struct TcpSettings
struct Tcp
{
bool enabled{true};
size_t port{1705};
std::vector<std::string> bind_to_address{{"0.0.0.0"}};
};
struct StreamSettings
struct Stream
{
size_t port{1704};
std::vector<std::string> pcmStreams;
@ -57,9 +66,10 @@ struct ServerSettings
std::string filter{"*:info"};
};
HttpSettings http;
TcpSettings tcp;
StreamSettings stream;
Server server;
Http http;
Tcp tcp;
Stream stream;
Logging logging;
};

View file

@ -25,9 +25,6 @@ Show version number
Daemonize
optional process priority [-20..19]
.TP
\fB--user arg\fR
the user[:group] to run snapserver as when daemonized
.TP
\fB-c, --config arg (=/etc/snapserver.conf)\fR
path to the configuration file
.SH FILES

View file

@ -63,57 +63,57 @@ int main(int argc, char* argv[])
#ifdef HAS_DAEMON
int processPriority(0);
auto daemonOption = op.add<Implicit<int>>("d", "daemon", "Daemonize\noptional process priority [-20..19]", 0, &processPriority);
auto userValue = op.add<Value<string>>("", "user", "the user[:group] to run snapserver as when daemonized", "");
#endif
op.add<Value<string>>("c", "config", "path to the configuration file", config_file, &config_file);
// debug settings
OptionParser conf("");
conf.add<Value<string>>("", "logging.sink", "log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
auto logfilterOption = conf.add<Value<string>>(
"", "logging.filter",
"log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
settings.logging.filter);
OptionParser conf("Overridable config file options");
// stream settings
conf.add<Value<size_t>>("", "stream.port", "Server port", settings.stream.port, &settings.stream.port);
auto streamValue = conf.add<Value<string>>(
"", "stream.stream", "URI of the PCM input stream.\nFormat: TYPE://host/path?name=NAME\n[&codec=CODEC]\n[&sampleformat=SAMPLEFORMAT]", pcmStream,
&pcmStream);
int num_threads = -1;
conf.add<Value<int>>("", "server.threads", "number of server threads", num_threads, &num_threads);
std::string pid_file = "/var/run/snapserver/pid";
conf.add<Value<string>>("", "server.pidfile", "pid file when running as daemon", pid_file, &pid_file);
std::string data_dir;
conf.add<Implicit<string>>("", "server.datadir", "directory where persistent data is stored", data_dir, &data_dir);
conf.add<Value<string>>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat);
conf.add<Value<string>>("", "stream.codec", "Default transport codec\n(flac|ogg|opus|pcm)[:options]\nType codec:? to get codec specific options",
settings.stream.codec, &settings.stream.codec);
// deprecated: stream_buffer, use chunk_ms instead
conf.add<Value<size_t>>("", "stream.stream_buffer", "Default stream read chunk size [ms]", settings.stream.streamChunkMs,
&settings.stream.streamChunkMs);
conf.add<Value<size_t>>("", "stream.chunk_ms", "Default stream read chunk size [ms]", settings.stream.streamChunkMs, &settings.stream.streamChunkMs);
conf.add<Value<int>>("", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs);
conf.add<Value<bool>>("", "stream.send_to_muted", "Send audio to muted clients", settings.stream.sendAudioToMutedClients,
&settings.stream.sendAudioToMutedClients);
auto stream_bind_to_address = conf.add<Value<string>>("", "stream.bind_to_address", "address for the server to listen on",
settings.stream.bind_to_address.front(), &settings.stream.bind_to_address[0]);
// server settings
conf.add<Value<int>>("", "server.threads", "number of server threads", settings.server.threads, &settings.server.threads);
conf.add<Value<string>>("", "server.pidfile", "pid file when running as daemon", settings.server.pid_file, &settings.server.pid_file);
conf.add<Value<string>>("", "server.user", "the user to run as when daemonized", settings.server.user, &settings.server.user);
conf.add<Implicit<string>>("", "server.group", "the group to run as when daemonized", settings.server.group, &settings.server.group);
conf.add<Implicit<string>>("", "server.datadir", "directory where persistent data is stored", settings.server.data_dir, &settings.server.data_dir);
// HTTP RPC settings
conf.add<Value<bool>>("", "http.enabled", "enable HTTP Json RPC (HTTP POST and websockets)", settings.http.enabled, &settings.http.enabled);
conf.add<Value<size_t>>("", "http.port", "which port the server should listen to", settings.http.port, &settings.http.port);
conf.add<Value<size_t>>("", "http.port", "which port the server should listen on", settings.http.port, &settings.http.port);
auto http_bind_to_address = conf.add<Value<string>>("", "http.bind_to_address", "address for the server to listen on",
settings.http.bind_to_address.front(), &settings.http.bind_to_address[0]);
conf.add<Implicit<string>>("", "http.doc_root", "serve a website from the doc_root location", settings.http.doc_root, &settings.http.doc_root);
// TCP RPC settings
conf.add<Value<bool>>("", "tcp.enabled", "enable TCP Json RPC)", settings.tcp.enabled, &settings.tcp.enabled);
conf.add<Value<size_t>>("", "tcp.port", "which port the server should listen to", settings.tcp.port, &settings.tcp.port);
conf.add<Value<size_t>>("", "tcp.port", "which port the server should listen on", settings.tcp.port, &settings.tcp.port);
auto tcp_bind_to_address = conf.add<Value<string>>("", "tcp.bind_to_address", "address for the server to listen on",
settings.tcp.bind_to_address.front(), &settings.tcp.bind_to_address[0]);
// stream settings
auto stream_bind_to_address = conf.add<Value<string>>("", "stream.bind_to_address", "address for the server to listen on",
settings.stream.bind_to_address.front(), &settings.stream.bind_to_address[0]);
conf.add<Value<size_t>>("", "stream.port", "which port the server should listen on", settings.stream.port, &settings.stream.port);
auto streamValue = conf.add<Value<string>>(
"", "stream.stream", "URI of the PCM input stream.\nFormat: TYPE://host/path?name=NAME\n[&codec=CODEC]\n[&sampleformat=SAMPLEFORMAT]", pcmStream,
&pcmStream);
conf.add<Value<string>>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat);
conf.add<Value<string>>("", "stream.codec", "Default transport codec\n(flac|ogg|opus|pcm)[:options]\nType codec:? to get codec specific options",
settings.stream.codec, &settings.stream.codec);
// deprecated: stream_buffer, use chunk_ms instead
conf.add<Value<size_t>>("", "stream.stream_buffer", "Default stream read chunk size [ms], deprecated, use stream.chunk_ms instead",
settings.stream.streamChunkMs, &settings.stream.streamChunkMs);
conf.add<Value<size_t>>("", "stream.chunk_ms", "Default stream read chunk size [ms]", settings.stream.streamChunkMs, &settings.stream.streamChunkMs);
conf.add<Value<int>>("", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs);
conf.add<Value<bool>>("", "stream.send_to_muted", "Send audio to muted clients", settings.stream.sendAudioToMutedClients,
&settings.stream.sendAudioToMutedClients);
// logging settings
conf.add<Value<string>>("", "logging.sink", "log sink [null,system,stdout,stderr,file:<filename>]", settings.logging.sink, &settings.logging.sink);
auto logfilterOption = conf.add<Value<string>>(
"", "logging.filter",
"log filter <tag>:<level>[,<tag>:<level>]* with tag = * or <log tag> and level = [trace,debug,info,notice,warning,error,fatal]",
settings.logging.filter);
try
{
op.parse(argc, argv);
@ -140,7 +140,7 @@ int main(int argc, char* argv[])
}
catch (const std::invalid_argument& e)
{
LOG(ERROR) << "Exception: " << e.what() << std::endl;
cerr << "Exception: " << e.what() << std::endl;
cout << "\n" << op << "\n";
exit(EXIT_FAILURE);
}
@ -234,35 +234,23 @@ int main(int argc, char* argv[])
std::unique_ptr<Daemon> daemon;
if (daemonOption->is_set())
{
string user = "";
string group = "";
if (settings.server.user.empty())
std::invalid_argument("user must not be empty");
if (userValue->is_set())
{
if (userValue->value().empty())
std::invalid_argument("user must not be empty");
if (settings.server.data_dir.empty())
settings.server.data_dir = "/var/lib/snapserver";
Config::instance().init(settings.server.data_dir, settings.server.user, settings.server.group);
vector<string> user_group = utils::string::split(userValue->value(), ':');
user = user_group[0];
if (user_group.size() > 1)
group = user_group[1];
}
if (data_dir.empty())
data_dir = "/var/lib/snapserver";
Config::instance().init(data_dir, user, group);
daemon.reset(new Daemon(user, group, pid_file));
LOG(NOTICE) << "daemonizing" << std::endl;
daemon->daemonize();
if (processPriority < -20)
processPriority = -20;
else if (processPriority > 19)
processPriority = 19;
daemon = std::make_unique<Daemon>(settings.server.user, settings.server.group, settings.server.pid_file);
processPriority = std::min(std::max(-20, processPriority), 19);
if (processPriority != 0)
setpriority(PRIO_PROCESS, 0, processPriority);
LOG(NOTICE) << "daemonizing" << std::endl;
daemon->daemonize();
LOG(NOTICE) << "daemon started" << std::endl;
}
else
Config::instance().init(data_dir);
Config::instance().init(settings.server.data_dir);
#else
Config::instance().init();
#endif
@ -299,9 +287,9 @@ int main(int argc, char* argv[])
auto streamServer = std::make_unique<StreamServer>(io_context, settings);
streamServer->start();
if (num_threads < 0)
num_threads = std::max(2, std::min(4, static_cast<int>(std::thread::hardware_concurrency())));
LOG(INFO) << "number of threads: " << num_threads << ", hw threads: " << std::thread::hardware_concurrency() << "\n";
if (settings.server.threads < 0)
settings.server.threads = std::max(2, std::min(4, static_cast<int>(std::thread::hardware_concurrency())));
LOG(INFO) << "number of threads: " << settings.server.threads << ", hw threads: " << std::thread::hardware_concurrency() << "\n";
// Construct a signal set registered for process termination.
boost::asio::signal_set signals(io_context, SIGHUP, SIGINT, SIGTERM);
@ -314,7 +302,7 @@ int main(int argc, char* argv[])
});
std::vector<std::thread> threads;
for (int n = 0; n < num_threads; ++n)
for (int n = 0; n < settings.server.threads; ++n)
threads.emplace_back([&] { io_context.run(); });
io_context.run();