From aea87601524d0081e261b2a6363cd88e9e8f4c60 Mon Sep 17 00:00:00 2001 From: randomdan Date: Tue, 31 Dec 2013 10:40:36 +0000 Subject: Add support for writing a pidfile, drop user privs and forking into a daemon --- project2/daemon/Jamfile.jam | 2 ++ project2/daemon/p2daemonAppEngine.cpp | 42 +++++++++++++++++++++++++++++++++-- project2/daemon/p2daemonAppEngine.h | 7 ++++++ project2/daemon/p2daemonMain.cpp | 36 ++++++++++++++++++++++++++++++ project2/daemon/pidfile.cpp | 42 +++++++++++++++++++++++++++++++++++ project2/daemon/pidfile.h | 16 +++++++++++++ project2/daemon/tempPrivs.cpp | 33 +++++++++++++++++++++++++++ project2/daemon/tempPrivs.h | 17 ++++++++++++++ 8 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 project2/daemon/pidfile.cpp create mode 100644 project2/daemon/pidfile.h create mode 100644 project2/daemon/tempPrivs.cpp create mode 100644 project2/daemon/tempPrivs.h 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 : : : : "`pkg-config --cflags glibmm-2.4`" "`pkg-config --libs glibmm-2.4`" ; +lib boost_filesystem : : boost_filesystem ; exe p2daemon : [ glob *.cpp ] : @@ -11,5 +12,6 @@ exe p2daemon : ../common//p2common ../cli//p2cli ../../libmisc + 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 #include #include +#include +#include +#include #include #include +#include "pidfile.h" +#include "tempPrivs.h" std::string DaemonAppEngine::daemonType; Glib::ustring DaemonAppEngine::reqPlatform; int DaemonAppEngine::periodicTimeout; DaemonAppEngine::SignalMap DaemonAppEngine::signalMap; +boost::optional DaemonAppEngine::setUser; +boost::optional DaemonAppEngine::setGroup; +boost::optional 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(); }), + "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(); }), + "Switch to this group on start up") +("daemon.pidfile", Options::functions([](const VariableType & p) { pidPath = p.as(); }, []{ 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; + if (DaemonAppEngine::pidPath) { + pidFile = boost::shared_ptr(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 #include +#include #include "lib/daemon.h" #include #include @@ -19,6 +20,10 @@ class DaemonAppEngine { static Glib::ustring reqPlatform; static std::string daemonType; static int periodicTimeout; + static boost::optional setUser; + static boost::optional setGroup; + static boost::optional pidPath; + static bool daemonize; protected: typedef boost::function SignalFunc; @@ -36,6 +41,8 @@ class DaemonAppEngine { std::thread * evThread; bool periodicCallback(); void shutdown(); + typedef boost::function 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 +#include +#include +#include + +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 + +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 + +class TempPrivs { + public: + TempPrivs(); + ~TempPrivs(); + + private: + const uid_t originalUid; + const gid_t originalGid; +}; + +#endif + -- cgit v1.2.3