check permissions for server.json

This commit is contained in:
badaix 2017-07-01 11:15:37 +02:00
parent f6209d7dce
commit 2955b20e9d
7 changed files with 230 additions and 163 deletions

View file

@ -50,13 +50,13 @@ else ifeq ($(TARGET), OPENWRT)
STRIP = echo
CXXFLAGS += -pthread -DNO_CPP11_STRING -DHAS_TREMOR -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON
LDFLAGS = -lasound -lvorbisidec -logg -lFLAC -lavahi-client -lavahi-common -latomic
OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o
OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o
else ifeq ($(TARGET), BUILDROOT)
CXXFLAGS += -pthread -DNO_CPP11_STRING -DHAS_TREMOR -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON
LDFLAGS += -lasound -lvorbisidec -logg -lFLAC -lavahi-client -lavahi-common -latomic
OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o
OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o
else ifeq ($(TARGET), MACOS)
@ -64,7 +64,7 @@ CXX = g++
STRIP = strip
CXXFLAGS += -DHAS_OGG -DHAS_COREAUDIO -DFREEBSD -DMACOS -DHAS_BONJOUR -DHAS_DAEMON -I/usr/local/include -Wno-unused-local-typedef -Wno-deprecated
LDFLAGS = -logg -lvorbis -lFLAC -L/usr/local/lib -framework AudioToolbox -framework CoreFoundation -framework IOKit
OBJ += player/coreAudioPlayer.o browseZeroConf/browseBonjour.o
OBJ += ../common/daemon.o player/coreAudioPlayer.o browseZeroConf/browseBonjour.o
else
@ -72,7 +72,7 @@ CXX = g++
STRIP = strip
CXXFLAGS += -pthread -DHAS_OGG -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON
LDFLAGS = -lrt -lasound -logg -lvorbis -lFLAC -lavahi-client -lavahi-common -static-libgcc -static-libstdc++
OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o
OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o
endif
@ -171,7 +171,7 @@ installsysv:
adduser:
@if ! getent passwd snapclient >/dev/null; then \
useradd --gid audio --system --home-dir /var/lib/snapclient snapclient; \
useradd --gid audio --system snapclient; \
fi; \
ifeq ($(TARGET), MACOS)

View file

@ -159,9 +159,10 @@ int main (int argc, char **argv)
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
#ifdef HAS_DAEMON
std::unique_ptr<Daemon> daemon;
if (daemonOption.isSet())
{
#ifdef HAS_DAEMON
string pidFile = "/var/run/snapclient/pid";
if (instance != 1)
pidFile += "." + cpt::to_string(instance);
@ -178,7 +179,8 @@ int main (int argc, char **argv)
if (user_group.size() > 1)
group = user_group[1];
}
daemonize(user, group, pidFile);
daemon.reset(new Daemon(user, group, pidFile));
daemon->daemonize();
if (processPriority < -20)
processPriority = -20;
else if (processPriority > 19)
@ -186,8 +188,8 @@ int main (int argc, char **argv)
if (processPriority != 0)
setpriority(PRIO_PROCESS, 0, processPriority);
logS(kLogNotice) << "daemon started" << std::endl;
#endif
}
#endif
PcmDevice pcmDevice = getPcmDevice(soundcard);
#if defined(HAS_ALSA)
@ -241,9 +243,6 @@ int main (int argc, char **argv)
}
logS(kLogNotice) << "daemon terminated." << endl;
#ifdef HAS_DAEMON
daemonShutdown();
#endif
exit(exitcode);
}

170
common/daemon.cpp Normal file
View file

@ -0,0 +1,170 @@
/***
This file is part of snapcast
Copyright (C) 2014-2017 Johannes Pohl
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 3 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, see <http://www.gnu.org/licenses/>.
***/
#include "daemon.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include <iostream>
#include "common/snapException.h"
#include "common/strCompat.h"
#include "common/utils.h"
Daemon::Daemon(const std::string& user, const std::string& group, const std::string& pidfile) :
pidFilehandle_(-1),
user_(user),
group_(group),
pidfile_(pidfile)
{
if (pidfile.empty() || pidfile.find('/') == std::string::npos)
throw SnapException("invalid pid file \"" + pidfile + "\"");
}
Daemon::~Daemon()
{
if (pidFilehandle_ != -1)
close(pidFilehandle_);
}
void Daemon::daemonize()
{
std::string pidfileDir(pidfile_.substr(0, pidfile_.find_last_of('/')));
mkdirRecursive(pidfileDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
/// Ensure only one copy
pidFilehandle_ = open(pidfile_.c_str(), O_RDWR|O_CREAT, 0644);
if (pidFilehandle_ == -1 )
{
/// Couldn't open lock file
throw SnapException("Could not open PID lock file \"" + pidfile_ + "\"");
}
uid_t user_uid = (uid_t)-1;
gid_t user_gid = (gid_t)-1;
std::string user_name;
#ifdef FREEBSD
bool had_group = false;
#endif
if (!user_.empty())
{
struct passwd *pwd = getpwnam(user_.c_str());
if (pwd == nullptr)
throw SnapException("no such user \"" + user_ + "\"");
user_uid = pwd->pw_uid;
user_gid = pwd->pw_gid;
user_name = strdup(user_.c_str());
/// this is needed by libs such as arts
setenv("HOME", pwd->pw_dir, true);
}
if (!group_.empty())
{
struct group *grp = getgrnam(group_.c_str());
if (grp == nullptr)
throw SnapException("no such group \"" + group_ + "\"");
user_gid = grp->gr_gid;
#ifdef FREEBSD
had_group = true;
#endif
}
if (chown(pidfile_.c_str(), user_uid, user_gid) == -1)
{
/// Couldn't open lock file
throw SnapException("Could not chown PID lock file \"" + pidfile_ + "\"");
}
/// set gid
if (user_gid != (gid_t)-1 && user_gid != getgid() && setgid(user_gid) == -1)
throw SnapException("Failed to set group " + cpt::to_string((int)user_gid));
//#if defined(FREEBSD) && !defined(MACOS)
//#ifdef FREEBSD
/// init supplementary groups
/// (must be done before we change our uid)
/// no need to set the new user's supplementary groups if we are already this user
// if (!had_group && user_uid != getuid() && initgroups(user_name, user_gid) == -1)
// throw SnapException("Failed to set supplementary groups of user \"" + user + "\"");
//#endif
/// set uid
if (user_uid != (uid_t)-1 && user_uid != getuid() && setuid(user_uid) == -1)
throw SnapException("Failed to set user " + user_);
/// Our process ID and Session ID
pid_t pid, sid;
/// Fork off the parent process
pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);
/// If we got a good PID, then we can exit the parent process.
if (pid > 0)
exit(EXIT_SUCCESS);
/// Change the file mode mask
umask(0);
/// Open any logs here
/// Create a new SID for the child process
sid = setsid();
if (sid < 0)
{
/// Log the failure
exit(EXIT_FAILURE);
}
/// Change the current working directory
if ((chdir("/")) < 0)
{
/// Log the failure
exit(EXIT_FAILURE);
}
/// Try to lock file
if (lockf(pidFilehandle_, F_TLOCK, 0) == -1)
throw SnapException("Could not lock PID lock file \"" + pidfile_ + "\"");
char str[10];
/// Get and format PID
sprintf(str, "%d\n", getpid());
/// write pid to lockfile
if (write(pidFilehandle_, str, strlen(str)) != (int)strlen(str))
throw SnapException("Could not write PID to lock file \"" + pidfile_ + "\"");
/// Close out the standard file descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}

View file

@ -16,149 +16,26 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef DAEMONIZE_H
#define DAEMONIZE_H
#ifndef DAEMON_H
#define DAEMON_H
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <grp.h>
#include "common/snapException.h"
#include "common/strCompat.h"
#include "common/utils.h"
#include <string>
int pidFilehandle;
void daemonize(const std::string& user, const std::string& group, const std::string& pidfile)
class Daemon
{
if (pidfile.empty() || pidfile.find('/') == std::string::npos)
throw SnapException("invalid pid file \"" + pidfile + "\"");
public:
Daemon(const std::string& user, const std::string& group, const std::string& pidfile);
virtual ~Daemon();
std::string pidfileDir(pidfile.substr(0, pidfile.find_last_of('/')));
mkdirRecursive(pidfileDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
/// Ensure only one copy
pidFilehandle = open(pidfile.c_str(), O_RDWR|O_CREAT, 0644);
if (pidFilehandle == -1 )
{
/// Couldn't open lock file
throw SnapException("Could not open PID lock file \"" + pidfile + "\"");
}
uid_t user_uid = (uid_t)-1;
gid_t user_gid = (gid_t)-1;
std::string user_name;
#ifdef FREEBSD
bool had_group = false;
#endif
if (!user.empty())
{
struct passwd *pwd = getpwnam(user.c_str());
if (pwd == nullptr)
throw SnapException("no such user \"" + user + "\"");
user_uid = pwd->pw_uid;
user_gid = pwd->pw_gid;
user_name = strdup(user.c_str());
/// this is needed by libs such as arts
setenv("HOME", pwd->pw_dir, true);
}
if (!group.empty()) {
struct group *grp = getgrnam(group.c_str());
if (grp == nullptr)
throw SnapException("no such group \"" + group + "\"");
user_gid = grp->gr_gid;
#ifdef FREEBSD
had_group = true;
#endif
}
if (chown(pidfile.c_str(), user_uid, user_gid) == -1)
{
/// Couldn't open lock file
throw SnapException("Could not chown PID lock file \"" + pidfile + "\"");
}
/// set gid
if (user_gid != (gid_t)-1 && user_gid != getgid() && setgid(user_gid) == -1)
throw SnapException("Failed to set group " + cpt::to_string((int)user_gid));
//#if defined(FREEBSD) && !defined(MACOS)
//#ifdef FREEBSD
/// init supplementary groups
/// (must be done before we change our uid)
/// no need to set the new user's supplementary groups if we are already this user
// if (!had_group && user_uid != getuid() && initgroups(user_name, user_gid) == -1)
// throw SnapException("Failed to set supplementary groups of user \"" + user + "\"");
//#endif
/// set uid
if (user_uid != (uid_t)-1 && user_uid != getuid() && setuid(user_uid) == -1)
throw SnapException("Failed to set user " + user);
/// Our process ID and Session ID
pid_t pid, sid;
/// Fork off the parent process
pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);
/// If we got a good PID, then we can exit the parent process.
if (pid > 0)
exit(EXIT_SUCCESS);
/// Change the file mode mask
umask(0);
/// Open any logs here
/// Create a new SID for the child process
sid = setsid();
if (sid < 0)
{
/// Log the failure
exit(EXIT_FAILURE);
}
/// Change the current working directory
if ((chdir("/")) < 0)
{
/// Log the failure
exit(EXIT_FAILURE);
}
/// Try to lock file
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
throw SnapException("Could not lock PID lock file \"" + pidfile + "\"");
char str[10];
/// Get and format PID
sprintf(str, "%d\n", getpid());
/// write pid to lockfile
if (write(pidFilehandle, str, strlen(str)) != (int)strlen(str))
throw SnapException("Could not write PID to lock file \"" + pidfile + "\"");
/// Close out the standard file descriptors
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
}
void daemonShutdown()
{
close(pidFilehandle);
}
#endif
void daemonize();
private:
int pidFilehandle_;
std::string user_;
std::string group_;
std::string pidfile_;
};
#endif // DAEMON_H

View file

@ -50,7 +50,7 @@ else ifeq ($(TARGET), OPENWRT)
STRIP = echo
CXXFLAGS += -DNO_CPP11_STRING -DHAS_AVAHI -DHAS_DAEMON -pthread
LDFLAGS += -lavahi-client -lavahi-common -latomic
OBJ += publishZeroConf/publishAvahi.o
OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o
else ifeq ($(TARGET), FREEBSD)
@ -58,7 +58,7 @@ CXX = g++
STRIP = echo
CXXFLAGS += -DNO_CPP11_STRING -DFREEBSD -DHAS_AVAHI -DHAS_DAEMON -pthread
LDFLAGS += -lrt -lavahi-client -lavahi-common -static-libgcc -static-libstdc++
OBJ += publishZeroConf/publishAvahi.o
OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o
else ifeq ($(TARGET), MACOS)
@ -66,7 +66,7 @@ CXX = g++
STRIP = strip
CXXFLAGS += -DFREEBSD -DMACOS -DHAS_BONJOUR -DHAS_DAEMON -Wno-deprecated -I/usr/local/include
LDFLAGS += -L/usr/local/lib -framework CoreFoundation -framework IOKit
OBJ += publishZeroConf/publishBonjour.o
OBJ += ../common/daemon.o publishZeroConf/publishBonjour.o
else
@ -74,7 +74,7 @@ CXX = g++
STRIP = strip
CXXFLAGS += -DHAS_AVAHI -DHAS_DAEMON -pthread
LDFLAGS = -lrt -lvorbis -lvorbisenc -logg -lFLAC -lavahi-client -lavahi-common -static-libgcc -static-libstdc++
OBJ += publishZeroConf/publishAvahi.o
OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o
endif

View file

@ -19,6 +19,7 @@
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <fstream>
#include <cerrno>
#include "common/snapException.h"
@ -34,13 +35,30 @@ Config::Config()
if (getenv("HOME") == NULL)
dir = "/var/lib/snapserver/";
else
dir = getenv("HOME") + string("/.config/snapserver/");
dir = getenv("HOME");
if (!dir.empty() && (dir.back() != '/'))
dir += "/";
if (dir.find("/var/lib/snapserver") == string::npos)
dir += ".config/snapserver/";
int status = mkdirRecursive(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if ((status != 0) && (errno != EEXIST))
throw SnapException("failed to create settings directory: \"" + dir + "\": " + cpt::to_string(errno));
filename_ = dir + "server.json";
logO << "Settings file: " << filename_ << "\n";
logS(kLogNotice) << "Settings file: \"" << filename_ << "\"\n";
int fd;
if ((fd = open(filename_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
{
if (errno == EACCES)
throw std::runtime_error("failed to open file \"" + filename_ + "\", permission denied");
else
throw std::runtime_error("failed to open file \"" + filename_ + "\"");
}
close(fd);
try
{

View file

@ -51,6 +51,7 @@ int main(int argc, char* argv[])
#ifdef MACOS
#pragma message "Warning: the macOS support is experimental and might not be maintained"
#endif
int exitcode = EXIT_SUCCESS;
try
{
@ -140,16 +141,16 @@ int main(int argc, char* argv[])
return 1;
}
Config::instance();
std::clog.rdbuf(new Log("snapserver", LOG_DAEMON));
signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
#ifdef HAS_DAEMON
std::unique_ptr<Daemon> daemon;
if (daemonOption.isSet())
{
#ifdef HAS_DAEMON
string user = "";
string group = "";
@ -163,7 +164,9 @@ int main(int argc, char* argv[])
if (user_group.size() > 1)
group = user_group[1];
}
daemonize(user, group, "/var/run/snapserver/pid");
daemon.reset(new Daemon(user, group, "/var/run/snapserver/pid"));
daemon->daemonize();
if (processPriority < -20)
processPriority = -20;
else if (processPriority > 19)
@ -171,8 +174,11 @@ int main(int argc, char* argv[])
if (processPriority != 0)
setpriority(PRIO_PROCESS, 0, processPriority);
logS(kLogNotice) << "daemon started" << std::endl;
#endif
}
#endif
Config::instance();
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
PublishZeroConf publishZeroConfg("Snapcast");
publishZeroConfg.publish({mDNSService("_snapcast._tcp", settings.port), mDNSService("_snapcast-jsonrpc._tcp", settings.controlPort)});
@ -206,9 +212,6 @@ int main(int argc, char* argv[])
}
logS(kLogNotice) << "daemon terminated." << endl;
#ifdef HAS_DAEMON
daemonShutdown();
#endif
exit(exitcode);
}