summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrandomdan <randomdan@localhost>2013-12-31 10:40:36 +0000
committerrandomdan <randomdan@localhost>2013-12-31 10:40:36 +0000
commitaea87601524d0081e261b2a6363cd88e9e8f4c60 (patch)
treeae273831936fc89c87bfc8394c34d50324fae8bb
parentAllow instanceSet to catch or propergate exceptions with onAll*, exceptions i... (diff)
downloadproject2-aea87601524d0081e261b2a6363cd88e9e8f4c60.tar.bz2
project2-aea87601524d0081e261b2a6363cd88e9e8f4c60.tar.xz
project2-aea87601524d0081e261b2a6363cd88e9e8f4c60.zip
Add support for writing a pidfile, drop user privs and forking into a daemon
-rw-r--r--project2/daemon/Jamfile.jam2
-rw-r--r--project2/daemon/p2daemonAppEngine.cpp42
-rw-r--r--project2/daemon/p2daemonAppEngine.h7
-rw-r--r--project2/daemon/p2daemonMain.cpp36
-rw-r--r--project2/daemon/pidfile.cpp42
-rw-r--r--project2/daemon/pidfile.h16
-rw-r--r--project2/daemon/tempPrivs.cpp33
-rw-r--r--project2/daemon/tempPrivs.h17
8 files changed, 193 insertions, 2 deletions
diff --git a/project2/daemon/Jamfile.jam b/project2/daemon/Jamfile.jam
index 123e6fc..1a159c3 100644
--- a/project2/daemon/Jamfile.jam
+++ b/project2/daemon/Jamfile.jam
@@ -2,6 +2,7 @@ alias glibmm : : : :
<cflags>"`pkg-config --cflags glibmm-2.4`"
<linkflags>"`pkg-config --libs glibmm-2.4`"
;
+lib boost_filesystem : : <name>boost_filesystem ;
exe p2daemon :
[ glob *.cpp ]
:
@@ -11,5 +12,6 @@ exe p2daemon :
<library>../common//p2common
<library>../cli//p2cli
<include>../../libmisc
+ <library>boost_filesystem
;
diff --git a/project2/daemon/p2daemonAppEngine.cpp b/project2/daemon/p2daemonAppEngine.cpp
index e5a1b3c..908b2b7 100644
--- a/project2/daemon/p2daemonAppEngine.cpp
+++ b/project2/daemon/p2daemonAppEngine.cpp
@@ -2,13 +2,25 @@
#include <logger.h>
#include <optionsSource.h>
#include <dlfcn.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
#include <options/flagSet.h>
#include <options/showHelp.h>
+#include "pidfile.h"
+#include "tempPrivs.h"
std::string DaemonAppEngine::daemonType;
Glib::ustring DaemonAppEngine::reqPlatform;
int DaemonAppEngine::periodicTimeout;
DaemonAppEngine::SignalMap DaemonAppEngine::signalMap;
+boost::optional<uid_t> DaemonAppEngine::setUser;
+boost::optional<gid_t> DaemonAppEngine::setGroup;
+boost::optional<boost::filesystem::path> DaemonAppEngine::pidPath;
+bool DaemonAppEngine::daemonize;
+
+SimpleMessageException(NoSuchUser);
+SimpleMessageException(NoSuchGroup);
DECLARE_OPTIONS(DaemonAppEngine, "Project2 Daemon options")
("help", new OptionFlagSet(&ShowHelpComponent::showHelp),
@@ -17,12 +29,32 @@ DECLARE_OPTIONS(DaemonAppEngine, "Project2 Daemon options")
("daemon.platform", Options::value(&reqPlatform), "Platform")
("daemon.periodicTimeout", Options::value(&periodicTimeout, 60),
"Delay between occurrences of component periodic calls (default 60s)")
+("daemon.setuser", Options::functions([](const VariableType & un) {
+ auto passwd = getpwnam(un);
+ if (!passwd) {
+ throw NoSuchUser(un);
+ }
+ setUser = passwd->pw_uid;
+ }, []{ setUser = boost::optional<uid_t>(); }),
+ "Switch to this user on start up")
+("daemon.setgroup", Options::functions([](const VariableType & gn) {
+ auto group = getgrnam(gn);
+ if (!group) {
+ throw NoSuchGroup(gn);
+ }
+ setGroup = group->gr_gid;
+ }, []{ setGroup = boost::optional<uid_t>(); }),
+ "Switch to this group on start up")
+("daemon.pidfile", Options::functions([](const VariableType & p) { pidPath = p.as<std::string>(); }, []{ pidPath.reset(); }),
+ "Write a pid file to this path")
+("daemon.daemonize", new OptionFlagSet(&daemonize),
+ "Detach and run as daemon")
END_OPTIONS(DaemonAppEngine);
DaemonAppEngine::DaemonAppEngine(int argc, char ** argv) :
- main_loop(Glib::MainLoop::create())
+ main_loop(Glib::MainLoop::create()),
+ daemonFactory(boost::bind(&DaemonLoader::create, DaemonLoader::getFor(daemonType), argc, argv))
{
- daemon = DaemonLoader::createNew(daemonType, argc, argv);
Glib::signal_timeout().connect_seconds(sigc::mem_fun(this, &DaemonAppEngine::periodicCallback), periodicTimeout);
}
@@ -53,6 +85,12 @@ DaemonAppEngine::shutdown()
void
DaemonAppEngine::process()
{
+ daemon = daemonFactory();
+ boost::shared_ptr<PidFile> pidFile;
+ if (DaemonAppEngine::pidPath) {
+ pidFile = boost::shared_ptr<PidFile>(new PidFile(*DaemonAppEngine::pidPath));
+ }
+ TempPrivs dropPrivs;
IgnoreSignal(SIGINT);
IgnoreSignal(SIGUSR1);
IgnoreSignal(SIGUSR2);
diff --git a/project2/daemon/p2daemonAppEngine.h b/project2/daemon/p2daemonAppEngine.h
index 0b16f70..84601bd 100644
--- a/project2/daemon/p2daemonAppEngine.h
+++ b/project2/daemon/p2daemonAppEngine.h
@@ -3,6 +3,7 @@
#include <options.h>
#include <boost/optional.hpp>
+#include <boost/filesystem/path.hpp>
#include "lib/daemon.h"
#include <thread>
#include <glibmm/main.h>
@@ -19,6 +20,10 @@ class DaemonAppEngine {
static Glib::ustring reqPlatform;
static std::string daemonType;
static int periodicTimeout;
+ static boost::optional<uid_t> setUser;
+ static boost::optional<gid_t> setGroup;
+ static boost::optional<boost::filesystem::path> pidPath;
+ static bool daemonize;
protected:
typedef boost::function<void(int)> SignalFunc;
@@ -36,6 +41,8 @@ class DaemonAppEngine {
std::thread * evThread;
bool periodicCallback();
void shutdown();
+ typedef boost::function<DaemonPtr()> DaemonFactory;
+ const DaemonFactory daemonFactory;
DaemonPtr daemon;
};
diff --git a/project2/daemon/p2daemonMain.cpp b/project2/daemon/p2daemonMain.cpp
index 20c4eb7..606945d 100644
--- a/project2/daemon/p2daemonMain.cpp
+++ b/project2/daemon/p2daemonMain.cpp
@@ -7,6 +7,39 @@
SimpleMessageException(UnsupportedArguments);
+static
+void
+daemonize()
+{
+ pid_t pid = fork();
+ if (pid < 0) {
+ Logger()->messagebf(LOG_ERR, "%s: Failed to fork a new process (%d:%s)", __PRETTY_FUNCTION__, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ if (setsid() < 0) {
+ Logger()->messagebf(LOG_ERR, "%s: Failed to set session Id (%d:%s)", __PRETTY_FUNCTION__, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ pid = fork();
+ if (pid < 0) {
+ Logger()->messagebf(LOG_ERR, "%s: Failed to fork a second new process (%d:%s)", __PRETTY_FUNCTION__, errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ // Other file handles presumably opened for a reason
+ close(0);
+ close(1);
+ close(2);
+}
+
int
main(int argc, char ** argv)
{
@@ -17,6 +50,9 @@ main(int argc, char ** argv)
//Plugable::onAllComponents(boost::bind(&ComponentLoader::onBefore, _1));
DaemonAppEngine dae(argc, argv);
+ if (DaemonAppEngine::daemonize) {
+ daemonize();
+ }
dae.process();
//Plugable::onAllComponents(boost::bind(&ComponentLoader::onIteration, _1));
diff --git a/project2/daemon/pidfile.cpp b/project2/daemon/pidfile.cpp
new file mode 100644
index 0000000..e2487c7
--- /dev/null
+++ b/project2/daemon/pidfile.cpp
@@ -0,0 +1,42 @@
+#include "pidfile.h"
+#include "p2daemonAppEngine.h"
+#include <unistd.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+SimpleMessageException(ChownFailed);
+
+PidFile::PidFile(const boost::filesystem::path & path) :
+ Path(path)
+{
+ auto pidDir = Path.parent_path();
+ if (!boost::filesystem::exists(pidDir)) {
+ boost::filesystem::create_directories(pidDir);
+ if (DaemonAppEngine::setGroup || DaemonAppEngine::setUser) {
+ if (chown(pidDir.string().c_str(),
+ DaemonAppEngine::setUser ? *DaemonAppEngine::setUser : -1,
+ DaemonAppEngine::setGroup ? *DaemonAppEngine::setGroup : -1)) {
+ boost::filesystem::remove(pidDir);
+ throw ChownFailed(strerror(errno));
+ }
+ }
+ }
+
+ boost::filesystem::ofstream pf(Path);
+ pf << getpid();
+ if (DaemonAppEngine::setGroup || DaemonAppEngine::setUser) {
+ if (chown(Path.string().c_str(),
+ DaemonAppEngine::setUser ? *DaemonAppEngine::setUser : -1,
+ DaemonAppEngine::setGroup ? *DaemonAppEngine::setGroup : -1)) {
+ boost::filesystem::remove(Path);
+ throw ChownFailed(strerror(errno));
+ }
+ }
+}
+
+PidFile::~PidFile()
+{
+ boost::filesystem::remove(Path);
+}
+
diff --git a/project2/daemon/pidfile.h b/project2/daemon/pidfile.h
new file mode 100644
index 0000000..108b0e7
--- /dev/null
+++ b/project2/daemon/pidfile.h
@@ -0,0 +1,16 @@
+#ifndef PIDFILE_H
+#define PIDFILE_H
+
+#include <boost/filesystem/path.hpp>
+
+class PidFile {
+ public:
+ PidFile(const boost::filesystem::path & path);
+ ~PidFile();
+
+ private:
+ const boost::filesystem::path Path;
+};
+
+#endif
+
diff --git a/project2/daemon/tempPrivs.cpp b/project2/daemon/tempPrivs.cpp
new file mode 100644
index 0000000..9c5edbd
--- /dev/null
+++ b/project2/daemon/tempPrivs.cpp
@@ -0,0 +1,33 @@
+#include "tempPrivs.h"
+#include "p2daemonAppEngine.h"
+
+TempPrivs::TempPrivs() :
+ originalUid(getuid()),
+ originalGid(getgid())
+{
+ if (DaemonAppEngine::setGroup) {
+ if (setegid(*DaemonAppEngine::setGroup)) {
+ throw std::runtime_error("Failed to set gid");
+ }
+ }
+ if (DaemonAppEngine::setUser) {
+ if (seteuid(*DaemonAppEngine::setUser)) {
+ throw std::runtime_error("Failed to set uid");
+ }
+ }
+}
+
+TempPrivs::~TempPrivs()
+{
+ if (DaemonAppEngine::setUser) {
+ if (seteuid(originalUid)) {
+ throw std::runtime_error("Failed restore uid");
+ }
+ }
+ if (DaemonAppEngine::setGroup) {
+ if (setegid(originalGid)) {
+ throw std::runtime_error("Failed restore gid");
+ }
+ }
+}
+
diff --git a/project2/daemon/tempPrivs.h b/project2/daemon/tempPrivs.h
new file mode 100644
index 0000000..af83fe6
--- /dev/null
+++ b/project2/daemon/tempPrivs.h
@@ -0,0 +1,17 @@
+#ifndef TEMPPRIVS_H
+#define TEMPPRIVS_H
+
+#include <sys/types.h>
+
+class TempPrivs {
+ public:
+ TempPrivs();
+ ~TempPrivs();
+
+ private:
+ const uid_t originalUid;
+ const gid_t originalGid;
+};
+
+#endif
+