diff --git a/client/snapclient.cpp b/client/snapclient.cpp index afb56f60..54f8b003 100644 --- a/client/snapclient.cpp +++ b/client/snapclient.cpp @@ -191,6 +191,7 @@ int main(int argc, char** argv) group = user_group[1]; } daemon = std::make_unique(user, group, pidFile); + SLOG(NOTICE) << "daemonizing" << std::endl; daemon->daemonize(); if (processPriority < -20) processPriority = -20; diff --git a/common/daemon.cpp b/common/daemon.cpp index 3e296d7c..6e12b71b 100644 --- a/common/daemon.cpp +++ b/common/daemon.cpp @@ -148,7 +148,7 @@ void Daemon::daemonize() /// Try to lock file if (lockf(pidFilehandle_, F_TLOCK, 0) == -1) - throw SnapException("Could not lock PID lock file \"" + pidfile_ + "\""); + throw SnapException("Could not lock PID lock file \"" + pidfile_ + "\". Is the daemon already running?"); char str[10]; /// Get and format PID diff --git a/common/popl.hpp b/common/popl.hpp index 6321a14d..f442b514 100644 --- a/common/popl.hpp +++ b/common/popl.hpp @@ -338,6 +338,9 @@ public: /// @param argv command line arguments void parse(int argc, const char* const argv[]); + /// Delete all parsed options + void reset(); + /// Produce a help message /// @param max_attribute show options up to this level (optional, advanced, expert) /// @return the help message @@ -989,11 +992,6 @@ inline void OptionParser::parse(const std::string& ini_filename) inline void OptionParser::parse(int argc, const char* const argv[]) { - unknown_options_.clear(); - non_option_args_.clear(); - for (auto& opt : options_) - opt->clear(); - for (int n = 1; n < argc; ++n) { const std::string arg(argv[n]); @@ -1094,6 +1092,15 @@ inline void OptionParser::parse(int argc, const char* const argv[]) } +inline void OptionParser::reset() +{ + unknown_options_.clear(); + non_option_args_.clear(); + for (auto& opt : options_) + opt->clear(); +} + + inline std::string OptionParser::help(const Attribute& max_attribute) const { ConsoleOptionPrinter option_printer(this); diff --git a/server/config.cpp b/server/config.cpp index 7c23c359..bb710469 100644 --- a/server/config.cpp +++ b/server/config.cpp @@ -49,13 +49,13 @@ void Config::init(const std::string& root_directory, const std::string& user, co else if (getenv("HOME") == nullptr) dir = "/var/lib/snapserver/"; else - dir = getenv("HOME"); + dir = string(getenv("HOME")) + "/.config/snapserver/"; if (!dir.empty() && (dir.back() != '/')) dir += "/"; - if (dir.find("/var/lib/snapserver") == string::npos) - dir += ".config/snapserver/"; + // if (dir.find("/var/lib/snapserver") == string::npos) + // dir += ".config/snapserver/"; int status = utils::file::mkdirRecursive(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if ((status != 0) && (errno != EEXIST)) diff --git a/server/etc/snapserver.conf b/server/etc/snapserver.conf index 6cf608b3..6c39c308 100644 --- a/server/etc/snapserver.conf +++ b/server/etc/snapserver.conf @@ -25,6 +25,16 @@ # in case there are a couple of longer running tasks, such as encoding # multiple audio streams #threads = -1 + +# the pid file when running as daemon +#pidfile = /var/run/snapserver/pid + +# directory where persistent data is stored (server.json) +# if empty, data dir will be +# - "/var/lib/snapserver/" when running as daemon +# - "$HOME/.config/snapserver/" when not running as daemon +#datadir = + # ############################################################################### @@ -99,7 +109,7 @@ # note that you need to have the librespot binary on your machine # sampleformat will be set to "44100:16:2" # file: file:///?name= -# process: process:///?name=[&wd_timeout=0][&log_stderr=false] +# process: process:///?name=[&wd_timeout=0][&log_stderr=false][¶ms=] # airplay: airplay:///?name=[&port=5000] # note that you need to have the airplay binary on your machine # sampleformat will be set to "44100:16:2" diff --git a/server/snapserver.cpp b/server/snapserver.cpp index de2548c9..c45fc161 100644 --- a/server/snapserver.cpp +++ b/server/snapserver.cpp @@ -75,21 +75,25 @@ int main(int argc, char* argv[]) &settings.logging.debug_logfile); // stream settings - conf.add>("p", "stream.port", "Server port", settings.stream.port, &settings.stream.port); + conf.add>("", "stream.port", "Server port", settings.stream.port, &settings.stream.port); auto streamValue = conf.add>( - "s", "stream.stream", "URI of the PCM input stream.\nFormat: TYPE://host/path?name=NAME\n[&codec=CODEC]\n[&sampleformat=SAMPLEFORMAT]", pcmStream, + "", "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>("", "server.threads", "number of server threads", num_threads, &num_threads); + std::string pid_file = "/var/run/snapserver/pid"; + conf.add>("", "server.pidfile", "pid file when running as daemon", pid_file, &pid_file); + std::string data_dir; + conf.add>("", "server.datadir", "directory where persistent data is stored", data_dir, &data_dir); conf.add>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat); - conf.add>("c", "stream.codec", "Default transport codec\n(flac|ogg|opus|pcm)[:options]\nType codec:? to get codec specific options", + conf.add>("", "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>("", "stream.stream_buffer", "Default stream read chunk size [ms]", settings.stream.streamChunkMs, &settings.stream.streamChunkMs); conf.add>("", "stream.chunk_ms", "Default stream read chunk size [ms]", settings.stream.streamChunkMs, &settings.stream.streamChunkMs); - conf.add>("b", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs); + conf.add>("", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs); conf.add>("", "stream.send_to_muted", "Send audio to muted clients", settings.stream.sendAudioToMutedClients, &settings.stream.sendAudioToMutedClients); auto stream_bind_to_address = conf.add>("", "stream.bind_to_address", "address for the server to listen on", @@ -108,12 +112,11 @@ int main(int argc, char* argv[]) auto tcp_bind_to_address = conf.add>("", "tcp.bind_to_address", "address for the server to listen on", settings.tcp.bind_to_address.front(), &settings.tcp.bind_to_address[0]); - // TODO: Should be possible to override settings on command line - try { op.parse(argc, argv); conf.parse(config_file); + conf.parse(argc, argv); if (tcp_bind_to_address->is_set()) { settings.tcp.bind_to_address.clear(); @@ -219,9 +222,11 @@ int main(int argc, char* argv[]) if (user_group.size() > 1) group = user_group[1]; } - - Config::instance().init("/var/lib/snapserver", user, group); - daemon.reset(new Daemon(user, group, "/var/run/snapserver/pid")); + if (data_dir.empty()) + data_dir = "/var/lib/snapserver"; + Config::instance().init(data_dir, user, group); + daemon.reset(new Daemon(user, group, pid_file)); + SLOG(NOTICE) << "daemonizing" << std::endl; daemon->daemonize(); if (processPriority < -20) processPriority = -20; @@ -232,7 +237,7 @@ int main(int argc, char* argv[]) SLOG(NOTICE) << "daemon started" << std::endl; } else - Config::instance().init(); + Config::instance().init(data_dir); #else Config::instance().init(); #endif diff --git a/server/stream_server.cpp b/server/stream_server.cpp index 85789bbc..4ece355f 100644 --- a/server/stream_server.cpp +++ b/server/stream_server.cpp @@ -94,6 +94,7 @@ void StreamServer::onChunkRead(const PcmStream* pcmStream, msg::PcmChunk* chunk, { // LOG(INFO) << "onChunkRead (" << pcmStream->getName() << "): " << duration << "ms\n"; bool isDefaultStream(pcmStream == streamManager_->getDefaultStream().get()); + // wrap it into a unique_ptr to ensure that the memory will be freed unique_ptr chunk_ptr(chunk); std::ostringstream oss; diff --git a/server/streamreader/stream_uri.cpp b/server/streamreader/stream_uri.cpp index da2280e4..93f0a339 100644 --- a/server/streamreader/stream_uri.cpp +++ b/server/streamreader/stream_uri.cpp @@ -48,7 +48,7 @@ void StreamUri::parse(const std::string& streamUri) uri = uri.substr(0, this->uri.length() - 1); string decodedUri = strutils::uriDecode(uri); - LOG(DEBUG) << "StreamUri: " << decodedUri << "\n"; + LOG(DEBUG) << "StreamUri decoded: " << decodedUri << "\n"; string tmp(decodedUri);