diff options
author | randomdan <randomdan@localhost> | 2013-12-31 10:40:36 +0000 |
---|---|---|
committer | randomdan <randomdan@localhost> | 2013-12-31 10:40:36 +0000 |
commit | 72f80a99d65356ed4b2b7d33c91744073f50960e (patch) | |
tree | ae273831936fc89c87bfc8394c34d50324fae8bb | |
parent | Allow instanceSet to catch or propergate exceptions with onAll*, exceptions i... (diff) | |
download | project2-72f80a99d65356ed4b2b7d33c91744073f50960e.tar.bz2 project2-72f80a99d65356ed4b2b7d33c91744073f50960e.tar.xz project2-72f80a99d65356ed4b2b7d33c91744073f50960e.zip |
Add support for writing a pidfile, drop user privs and forking into a daemon
-rw-r--r-- | project2/daemon/Jamfile.jam | 2 | ||||
-rw-r--r-- | project2/daemon/p2daemonAppEngine.cpp | 42 | ||||
-rw-r--r-- | project2/daemon/p2daemonAppEngine.h | 7 | ||||
-rw-r--r-- | project2/daemon/p2daemonMain.cpp | 36 | ||||
-rw-r--r-- | project2/daemon/pidfile.cpp | 42 | ||||
-rw-r--r-- | project2/daemon/pidfile.h | 16 | ||||
-rw-r--r-- | project2/daemon/tempPrivs.cpp | 33 | ||||
-rw-r--r-- | project2/daemon/tempPrivs.h | 17 |
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 + |