// Copyright (c) 1999-2002 David Muse
// See the COPYING file for more information
#include <rudiments/daemonprocess.h>
#include <rudiments/file.h>
#include <rudiments/charstring.h>
#include <rudiments/passwdentry.h>
#include <rudiments/groupentry.h>
#include <rudiments/snooze.h>
#include <rudiments/process.h>
#include <rudiments/error.h>
// for umask
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
// for fork/exit...
#ifdef RUDIMENTS_HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifndef MINGW32
// for wait...
#include <sys/wait.h>
#endif
#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif
class daemonprocessprivate {
friend class daemonprocess;
private:
};
// LAME: not in class
static signalhandler _deadchildhandler;
static signalhandler _shutdownhandler;
static signalhandler _crashhandler;
static void (*_shutdownfunc)(int);
static void (*_crashfunc)(int);
daemonprocess::daemonprocess() {
pvt=new daemonprocessprivate;
// FIXME: setting static members in the constructor???
// we want daemons to wait for children to die before shutting down,
// so register some default shutdown/crash handlers that only do that
// FIXME: it should be possible to use one of the C++ casting operators
// here (and elsewhere in this class), but I'm not really sure how...
_shutdownhandler.setHandler((void *)defaultShutDown);
_shutdownhandler.handleSignal(SIGINT);
_shutdownhandler.handleSignal(SIGTERM);
_crashhandler.setHandler((void *)defaultCrash);
_crashhandler.handleSignal(SIGSEGV);
waitForChildren();
}
daemonprocess::~daemonprocess() {
delete pvt;
}
bool daemonprocess::createPidFile(const char *filename, mode_t permissions) {
char *pid=charstring::parseNumber((uint64_t)process::getProcessId());
bool retval=(file::createFile(filename,permissions,pid)==
(ssize_t)charstring::length(pid));
delete[] pid;
return retval;
}
int64_t daemonprocess::checkForPidFile(const char *filename) {
char *pidstring=file::getContents(filename);
int64_t retval=(pidstring && pidstring[0])?
charstring::toInteger(pidstring):-1;
delete[] pidstring;
return retval;
}
#ifndef MINGW32
bool daemonprocess::detach() const {
// fork off a child process
int result;
do {
result=fork();
} while (result==-1 && error::getErrorNumber()==EINTR);
if (result==-1) {
return false;
}
// let the parent process exit
if (result) {
// cygwin needs a sleep or both processes will exit
#ifdef __CYGWIN__
snooze::macrosnooze(1);
#endif
_exit(0);
}
// become process group and session group leader
// with no controlling terminal
setsid();
// change directory to root to avoid keeping any directories in use
do {} while (chdir("/")==-1 && error::getErrorNumber()==EINTR);
// Set umask such that files are created 666 and directories 777.
// This way we can change them to whatever we like using chmod().
// We want to avoid inheriting a umask which wouldn't give us write
// permissions to files we create.
umask(0);
return true;
}
#else
bool daemonprocess::detach() const {
// FIXME: implement this
return true;
}
#endif
void daemonprocess::handleShutDown(void *shutdownfunction) {
_shutdownfunc=(void(*)(int))shutdownfunction;
_shutdownhandler.setHandler((void *)shutDown);
_shutdownhandler.handleSignal(SIGINT);
_shutdownhandler.handleSignal(SIGTERM);
}
void daemonprocess::handleCrash(void *crashfunction) {
_crashfunc=(void(*)(int))crashfunction;
_crashhandler.setHandler((void *)crash);
_crashhandler.handleSignal(SIGSEGV);
}
#ifndef MINGW32
void daemonprocess::waitForChildrenToExit() {
// Some systems generate a single SIGCHLD even if more than 1 child
// has entered it's exit state, so we need to loop here and catch
// all of them.
// If waitpid() returns 0 then there were no more processes in their
// exit state, the loop should exit.
// If it returns > 0 then it successfully waited on a process and it
// should loop back to wait on another one.
// If it returns -1 and was interrupted by a signal and should loop
// back and be restarted.
// If it returns -1 then there was some other error and the loop should
// exit.
for (;;) {
int pid=waitpid(-1,NULL,WNOHANG);
if (pid==0 || (pid==-1 && error::getErrorNumber()!=EINTR)) {
break;
}
}
// FIXME: What if a SIGCHLD gets generated after waitpid() has returned
// but before the signal handler exits? Will that SIGCHLD be lost?
// Since we didn't use the SA_ONESHOT flag when we set up this signal
// handler, we don't need to reinstall the signal handler here, it will
// be done automatically.
}
#else
void daemonprocess::waitForChildrenToExit() {
// FIXME: implement this...
// Use ChildStart()
}
#endif
void daemonprocess::shutDown() {
waitForChildren();
(*_shutdownfunc)(0);
}
void daemonprocess::crash() {
waitForChildren();
(*_crashfunc)(0);
}
void daemonprocess::defaultShutDown() {
waitForChildren();
exit(0);
}
void daemonprocess::defaultCrash() {
waitForChildren();
exit(1);
}
#ifndef MINGW32
void daemonprocess::waitForChildren() {
_deadchildhandler.setHandler((void *)waitForChildrenToExit);
_deadchildhandler.addFlag(SA_NOCLDSTOP);
_deadchildhandler.handleSignal(SIGCHLD);
}
void daemonprocess::dontWaitForChildren() {
_deadchildhandler.setHandler((void *)SIG_DFL);
_deadchildhandler.removeAllFlags();
_deadchildhandler.handleSignal(SIGCHLD);
}
#else
void daemonprocess::waitForChildren() {
// FIXME: implement this
}
void daemonprocess::dontWaitForChildren() {
// FIXME: implement this
}
#endif
int daemonprocess::runAsUser(const char *username) const {
uid_t userid;
return (passwdentry::getUserId(username,&userid))?
runAsUserId(userid):1;
}
int daemonprocess::runAsGroup(const char *groupname) const {
gid_t groupid;
return (groupentry::getGroupId(groupname,&groupid))?
runAsGroupId(groupid):1;
}
int daemonprocess::runAsUserId(uid_t uid) const {
return process::setUserId(uid);
}
int daemonprocess::runAsGroupId(gid_t gid) const {
return process::setGroupId(gid);
}
#ifdef RUDIMENTS_NAMESPACE
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1