Changes: Implementation of re-exec functionality, trigger is disabled

RevBy: Alexey Shilov, Juha Lintula
pull/1/head
Pertti Kellomäki 14 years ago committed by Alexey Shilov
parent 0899a62e27
commit b8bc03c0eb

@ -59,10 +59,32 @@ else (EXISTS ${CREDS_H})
message(STATUS " not found: Platform Security is disabled.")
endif (EXISTS ${CREDS_H})
# Find aegis_crypto.h (and libaegis_crypto if the header is found)
message(STATUS "checking for aegis_crypto.h")
find_file(AEGIS_CRYPTO_H NAMES aegis_crypto.h PATHS /usr/include/sys)
if (EXISTS ${AEGIS_CRYPTO_H})
message(STATUS " found: " ${AEGIS_CRYPTO_H})
message(STATUS "checking for libaegis_crypto")
find_library(LIBAEGIS_CRYPTO NAMES aegis_crypto)
if (EXISTS ${LIBAEGIS_CRYPTO})
add_definitions(-DHAVE_AEGIS_CRYPTO)
link_libraries(${LIBAEGIS_CRYPTO})
message(STATUS " found: " ${LIBAEGIS_CRYPTO})
else (EXISTS ${LIBAEGIS_CRYPTO})
message(STATUS " not found: Platform Security is disabled.")
endif (EXISTS ${LIBAEGIS_CRYPTO})
else (EXISTS ${AEGIS_CRYPTO_H})
message(STATUS " not found: Platform Security is disabled.")
endif (EXISTS ${AEGIS_CRYPTO_H})
if ($ENV{DISABLE_VERIFICATION})
add_definitions(-DDISABLE_VERIFICATION)
endif ($ENV{DISABLE_VERIFICATION})
if ($ENV{DEBUG_BUILD})
add_definitions(-DDEBUG_BUILD)
endif ($ENV{DEBUG_BUILD})
# Set the program name defines. Must be at this level due to unit tests.
add_definitions(-DPROG_NAME_INVOKER="invoker")
add_definitions(-DPROG_NAME_LAUNCHER="applauncherd")
@ -124,7 +146,11 @@ install(DIRECTORY examples
DESTINATION /usr/share/doc/applauncherd)
# Install startup script
install(PROGRAMS src/scripts/applauncherd DESTINATION /usr/bin/)
if ($ENV{DEBUG_BUILD})
install(PROGRAMS src/scripts/applauncherd-debug DESTINATION /usr/bin/ RENAME applauncherd)
else ($ENV{DEBUG_BUILD})
install(PROGRAMS src/scripts/applauncherd DESTINATION /usr/bin/)
endif ($ENV{DEBUG_BUILD})
# Install pkg-config and qmake feature files. Note that current packaging policy is such
# that applauncherd-dev installs only qt-boostable.pc which is intended for "all"
@ -142,3 +168,6 @@ install(FILES data/mkspecs/features/qdeclarative-boostable.prf DESTINATION /usr/
if ($ENV{MEEGO})
install(FILES meego/applauncherd.desktop DESTINATION /etc/xdg/autostart)
endif ($ENV{MEEGO})
# Install aegisfs configuration file
install(FILES aegisfs/applauncherd-fs.conf DESTINATION /etc/aegisfs.d)

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<aegisfs>
<mount point="/var/run/applauncherd" type="private" prot="encrypted">
<pstore name="applauncherd-protected" type="private" flatten="no" />
<access type="read" require="applauncherd-launcher::access" />
<access type="write" require="applauncherd-launcher::access" />
</mount>
</aegisfs>

@ -6,7 +6,12 @@
<request>
<credential match="CAP::*"/>
<credential name="Cellular"/>
<credential name="access"/>
<for path="/usr/bin/applauncherd.bin"/>
</request>
<request context="INSTALL">
<credential name="aegisfs::AegisFSMountAdd" />
<credential name="CAP::kill" />
</request>
</aegis>

@ -7,3 +7,4 @@ usr/lib/applauncherd/libqtbooster.so
usr/lib/applauncherd/libqdeclarativebooster.so
usr/lib/libmdeclarativecache.so
usr/lib/libmdeclarativecache.so.*
/etc/aegisfs.d

@ -0,0 +1,15 @@
#!/bin/sh
if [ "$1" = "configure" ]; then
# At package configuration time we need to inform aegis about the configuration file
aegisfs --install /etc/aegisfs.d/applauncherd-fs.conf
elif [ "$1" = "triggered" ]; then
# Installation of some other package may have caused applauncherd credentials to go stale,
# ask applauncherd to re-exec to refresh the credentials.
if [ -f /var/run/applauncherd.lock ]; then
#kill -HUP `cat /var/run/applauncherd.lock`
echo "" #placeholder
fi
fi

@ -0,0 +1,2 @@
# aegis-install generates this trigger when there is a potential policy change for applauncherd
interest aegis.applauncherd-launcher.

2
debian/changelog vendored

@ -1,6 +1,6 @@
applauncherd (2.0.5) unstable; urgency=low
*
* Changes: Implementation of re-exec functionality, trigger is disabled
-- Alexey Shilov <alexey.shilov@nokia.com> Mon, 10 Oct 2011 16:03:44 +0300

2
debian/control vendored

@ -2,7 +2,7 @@ Source: applauncherd
Section: admin
Priority: important
Maintainer: Alexey Shilov <alexey.shilov@nokia.com>
Build-Depends: cmake (>= 2.6.0), debhelper (>= 7), libqt4-dev (>= 4.5.0), libmeegotouch-dev, libcreds2-dev [arm armel], aegis-builder (>= 1.4) [arm armel], libxtst-dev, libxext-dev, libxi-dev, doxygen, python2.6, libsaveas, libmeegocontrol0, libduicontrolpanel, libxcomposite-dev, libresource0, libqt4-dev, liblocationextras, libmessagingif0, libsignon-qt0, libcontactswidgets-dev, sensord, libc6, libiphb0, libsignon-ui0, libdatepicker2, libxrandr-dev, liblocationpicker, libresource-dev
Build-Depends: cmake (>= 2.6.0), debhelper (>= 7), libqt4-dev (>= 4.5.0), libmeegotouch-dev, libcreds2-dev [arm armel], aegis-builder (>= 1.4) [arm armel], libaegis-crypto-dev, libxtst-dev, libxext-dev, libxi-dev, doxygen, python2.6, libsaveas, libmeegocontrol0, libduicontrolpanel, libxcomposite-dev, libresource0, libqt4-dev, liblocationextras, libmessagingif0, libsignon-qt0, libcontactswidgets-dev, sensord, libc6, libiphb0, libsignon-ui0, libdatepicker2, libxrandr-dev, liblocationpicker, libresource-dev
Standards-Version: 3.8.0
Package: applauncherd

10
debian/rules vendored

@ -6,19 +6,23 @@
# architecture we're building for
DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH)
ifneq (,$(filter debug,$(DEB_BUILD_OPTIONS)))
DEBUG_BUILD=1
endif
configure: configure-stamp
configure-stamp:
dh_testdir
ifeq ($(DEB_HOST_ARCH), arm)
# Configure Applauncherd to be build with tests
BUILD_TESTS=1 HARMATTAN=1 ./configure
BUILD_TESTS=1 HARMATTAN=1 DEBUG_BUILD=$(DEBUG_BUILD) ./configure
else ifeq ($(DEB_HOST_ARCH), armel)
# Configure Applauncherd to be build with tests
BUILD_TESTS=1 HARMATTAN=1 ./configure
BUILD_TESTS=1 HARMATTAN=1 DEBUG_BUILD=$(DEBUG_BUILD) ./configure
else
# Configure Applauncherd to be build with tests and without checking invoker credentials
BUILD_TESTS=1 DISABLE_VERIFICATION=1 HARMATTAN=1 ./configure
BUILD_TESTS=1 DISABLE_VERIFICATION=1 HARMATTAN=1 DEBUG_BUILD=$(DEBUG_BUILD) ./configure
endif
touch configure-stamp

@ -38,18 +38,31 @@
#include <cstring>
#include <cstdio>
#include <stdexcept>
#include <fstream>
#include <sstream>
#ifdef HAVE_AEGIS_CRYPTO
#include <aegis_crypto.h>
#endif
#include "coverage.h"
// Environment
extern char ** environ;
Daemon * Daemon::m_instance = NULL;
int Daemon::m_lockFd = -1;
const int Daemon::m_boosterSleepTime = 2;
const char *Daemon::m_stateDir = "/var/run/applauncherd";
const char *Daemon::m_stateFile = "/var/run/applauncherd/saved-state";
Daemon::Daemon(int & argc, char * argv[]) :
m_daemon(false),
m_debugMode(false),
m_bootMode(false),
m_socketManager(new SocketManager),
m_singleInstance(new SingleInstance)
m_singleInstance(new SingleInstance),
m_reExec(false)
{
if (!Daemon::m_instance)
{
@ -63,16 +76,21 @@ Daemon::Daemon(int & argc, char * argv[]) :
// Parse arguments
parseArgs(ArgVect(argv, argv + argc));
if (m_reExec)
{
restoreState();
}
// Store arguments list
m_initialArgv = argv;
m_initialArgc = argc;
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, m_boosterLauncherSocket) == -1)
if (!m_reExec && socketpair(AF_UNIX, SOCK_DGRAM, 0, m_boosterLauncherSocket) == -1)
{
throw std::runtime_error("Daemon: Creating a socket pair for boosters failed!\n");
}
if (pipe(m_sigPipeFd) == -1)
if (!m_reExec && pipe(m_sigPipeFd) == -1)
{
throw std::runtime_error("Daemon: Creating a pipe for Unix signals failed!\n");
}
@ -104,6 +122,11 @@ bool Daemon::lock()
if(fcntl(m_lockFd, F_SETLK, &fl) == -1)
return false;
// write pid into the lock file
std::stringstream ss;
ss << getpid();
write(m_lockFd, ss.str().c_str(), ss.str().length());
return true;
}
@ -153,14 +176,23 @@ void Daemon::run()
// dlopen() booster plugins
loadBoosterPlugins();
// Create sockets for each of the boosters
initBoosterSockets();
// dlopen single-instance
loadSingleInstancePlugin();
// Fork each booster for the first time
forkBoosters();
if (m_reExec)
{
// Reap dead booster processes and restart them
// Note: this cannot be done before booster plugins have been loaded
reapZombies();
}
else
{
// Create sockets for each of the boosters
initBoosterSockets();
// Fork each booster for the first time
forkBoosters();
}
// Main loop
while (true)
@ -223,6 +255,14 @@ void Daemon::run()
Logger::logDebug("Daemon: SIGPIPE received.");
break;
case SIGHUP:
Logger::logDebug("Daemon: SIGHUP received.");
reExec();
// not reached if re-exec successful
break;
default:
break;
}
@ -668,11 +708,16 @@ void Daemon::parseArgs(const ArgVect & args)
else if ((*i) == "--debug")
{
Logger::setDebugMode(true);
m_debugMode = true;
}
else if ((*i) == "--help" || (*i) == "-h")
{
usage(EXIT_SUCCESS);
}
else if ((*i) == "--re-exec")
{
m_reExec = true;
}
}
}
@ -753,7 +798,15 @@ void Daemon::setUnixSignalHandler(int signum, sighandler_t handler)
{
sighandler_t old_handler = signal(signum, handler);
if (old_handler != SIG_ERR)
if (signum == SIGHUP && old_handler == SIG_IGN)
{
// SIGHUP is a special case. It is set to SIG_IGN
// when applauncherd does a re-exec, but we still want the
// boosters / launched applications to get the default
// handler.
m_originalSigHandlers[signum] = SIG_DFL;
}
else if (old_handler != SIG_ERR)
{
m_originalSigHandlers[signum] = old_handler;
}
@ -783,3 +836,286 @@ Daemon::~Daemon()
__gcov_flush();
#endif
}
void Daemon::reExec()
{
Logger::logInfo("Daemon: Re-exec requested.");
struct stat st;
if (stat(m_stateDir, &st) != 0)
{
Logger::logDebug("Daemon: State saving directory %s does not exist", m_stateDir);
Logger::logDebug("Daemon: Attempting to create it");
if (mkdir(m_stateDir, S_IRUSR | S_IWUSR | S_IXUSR) != 0)
{
Logger::logDebug("Daemon: Failed to create directory, re-exec failed, exiting.");
_exit(1);
}
}
if (stat(m_stateDir, &st) != 0)
{
Logger::logDebug("Daemon: Directory vanished, re-exec failed, exiting.");
_exit(1);
}
if (!S_ISDIR(st.st_mode))
{
Logger::logDebug("Daemon: %s exists but it is not a directory, re-exec failed, exiting.", m_stateDir);
_exit(1);
}
try {
std::ofstream ss(m_stateFile);
ss.exceptions (std::ifstream::failbit | std::ifstream::badbit);
// dump the pid to double check that the state file is from this process
ss << "my-pid " << getpid() << std::endl;
// Save debug mode first, restoring it will enable debug logging.
// This way we get debug output from the re-execed daemon as early
// as possible.
ss << "debug-mode " << m_debugMode << std::endl;
// The pids of the dead boosters are also passed as children, but
// this causes no harm.
for(PidVect::iterator it = m_children.begin(); it != m_children.end(); it++)
{
ss << "child " << *it << std::endl;
}
for(PidMap::iterator it = m_boosterPidToInvokerPid.begin(); it != m_boosterPidToInvokerPid.end(); it++)
{
ss << "booster-invoker-pid " << it->first << " " << it->second << std::endl;
}
for(FdMap::iterator it = m_boosterPidToInvokerFd.begin(); it != m_boosterPidToInvokerFd.end(); it++)
{
ss << "booster-invoker-fd " << it->first << " " << it->second << std::endl;
}
for(TypeMap::iterator it = m_boosterTypeToPid.begin(); it != m_boosterTypeToPid.end(); it++)
{
ss << "booster-type " << (it->first) << " " << it->second << std::endl;
}
ss << "launcher-socket " << m_boosterLauncherSocket[0] << " " << m_boosterLauncherSocket[1] << std::endl;
ss << "sigpipe-fd " << m_sigPipeFd[0] << " " << m_sigPipeFd[1] << std::endl;
ss << "boot-mode " << m_bootMode << std::endl;
ss << "lock-file " << m_lockFd << std::endl;
SocketManager::SocketHash s = m_socketManager->getState();
for(SocketManager::SocketHash::iterator it = s.begin(); it != s.end(); it++)
{
ss << "socket-hash " << it->first << " " << it->second << std::endl;
}
// when the new applauncherd reads this,
// it knows state saving was successful.
ss << "end" << std::endl;
ss.close();
}
catch (std::ofstream::failure e)
{
Logger::logError("Daemon: Failed to save state, re-exec failed, exiting.");
_exit(1);
}
char *argv[] = {"/usr/bin/applauncherd.bin",
"--re-exec",
" ",
NULL};
// The boosters have state which will become stale, so kill them.
// The dead boosters will be reaped when the re-execed applauncherd
// calls reapZombies after it has initialized.
killBoosters();
// Signal handlers are reset at exec(), so we will lose
// the SIGHUP handling. However, ignoring a signal is preserved
// over exec(), so start ignoring SIGHUP to prevent applauncherd
// dying if we receive multiple SIGHUPs.
signal(SIGHUP, SIG_IGN);
Logger::logDebug("Daemon: configuration saved succesfully, call execve() ");
execve(argv[0], argv, environ);
// Not reached.
Logger::logDebug("Daemon: Failed to execute execve(), re-exec failed, exiting.");
_exit(1);
}
void Daemon::restoreState()
{
#ifdef HAVE_AEGIS_CRYPTO
aegis_system_mode_t aegisMode;
if (aegis_crypto_verify_aegisfs(m_stateDir, &aegisMode) != 0
|| aegisMode != aegis_system_protected)
{
#ifndef DEBUG_BUILD
Logger::logError("Daemon: State file not on aegis protected file system, exiting.");
_exit(1);
#else
Logger::logDebug("Daemon: State file not on aegis protected file system, on non-debug build I would die now.");
#endif
}
#endif
try
{
// We have saved state, try to restore it.
std::ifstream ss(m_stateFile);
ss.exceptions (std::ifstream::failbit | std::ifstream::badbit);
std::string token;
ss >> token;
// Bit of defensive programming. Read the pid of the process
// that left the state file. If it is different from my pid,
// then something is wrong, and we better exit.
if (token != "my-pid")
{
throw "Daemon: malformed state file, exiting.";
}
else
{
int pid;
ss >> pid;
if (pid != getpid())
{
throw "Daemon: stale state file, exiting.";
}
}
// Do not check for eof, instead trigger an exception
// if reading past end of file. When state restore is
// successful we read the "end" token and return from
// this method.
while (true)
{
ss >> token;
if (token == "end")
{
// successfully restored state
ss.close();
// In debug mode it is better to leave the file there
// so it can be examined.
if (!m_debugMode && remove(m_stateFile) == -1)
{
Logger::logError("Daemon: could not remove state file %s", m_stateFile);
}
Logger::logDebug("Daemon: state restore completed");
return;
}
else if (token == "child")
{
int arg1;
ss >> arg1;
Logger::logDebug("Daemon: restored child %d", arg1);
m_children.push_back(arg1);
}
else if (token == "booster-invoker-pid")
{
int arg1, arg2;
ss >> arg1;
ss >> arg2;
Logger::logDebug("Daemon: restored m_boosterPidToInvokerPid[%d] = %d", arg1, arg2);
m_boosterPidToInvokerPid[arg1] = arg2;
}
else if (token == "booster-invoker-fd")
{
int arg1, arg2;
ss >> arg1;
ss >> arg2;
Logger::logDebug("Daemon: restored m_boosterPidToInvokerFd[%d] = %d", arg1, arg2);
m_boosterPidToInvokerFd[arg1] = arg2;
}
else if (token == "booster-type")
{
std::string arg1;
int arg2;
ss >> arg1;
ss >> arg2;
Logger::logDebug("Daemon: restored m_boosterTypeToPid[%c] = %d", arg1[0], arg2);
m_boosterTypeToPid[arg1[0]] = arg2;
}
else if (token == "launcher-socket")
{
int arg1, arg2;
ss >> arg1;
ss >> arg2;
Logger::logDebug("Daemon: restored m_boosterLauncherSocket[] = {%d, %d}", arg1, arg2);
m_boosterLauncherSocket[0] = arg1;
m_boosterLauncherSocket[1] = arg2;
}
else if (token == "sigpipe-fd")
{
int arg1, arg2;
ss >> arg1;
ss >> arg2;
Logger::logDebug("Daemon: restored m_sigPipeFd[] = {%d, %d}", arg1, arg2);
m_sigPipeFd[0] = arg1;
m_sigPipeFd[1] = arg2;
}
else if (token == "lock-file")
{
int arg1;
ss >> arg1;
Logger::logDebug("Daemon: restored m_lockFd = %d", arg1);
Daemon::m_lockFd = arg1;
}
else if (token == "socket-hash")
{
std::string arg1;
int arg2;
ss >> arg1;
ss >> arg2;
m_socketManager->addMapping(arg1, arg2);
Logger::logDebug("Daemon: restored socketHash[%s] = %d", arg1.c_str(), arg2);
}
else if (token == "debug-mode")
{
bool arg1;
ss >> arg1;
m_debugMode = arg1;
Logger::setDebugMode(m_debugMode);
Logger::logDebug("Daemon: restored m_debugMode = %d", arg1);
}
else if (token == "boot-mode")
{
bool arg1;
ss >> arg1;
m_bootMode = arg1;
Logger::logDebug("Daemon: restored m_bootMode = %d", arg1);
}
}
}
catch (std::ifstream::failure e)
{
// Ran out of saved state before "end" token
// or there was some other error in restoring the sate.
Logger::logError("Daemon: Failed to restore saved state, exiting.");
}
catch (char *err)
{
// Some other error, e.g. stale state file
Logger::logError(err);
}
// In debug mode it is better to leave the file there
// so it can be examined.
if (!m_debugMode && remove(m_stateFile) == -1)
{
Logger::logError("Daemon: could not remove state file %s", m_stateFile);
}
// This is only reached if state restore was unsuccessful.
_exit(1);
}

@ -169,9 +169,18 @@ private:
//! Prints the usage and exits with given status
void usage(int status);
//! Re-exec applauncherd.bin
void reExec();
//! Restore state.
void restoreState();
//! Daemonize flag (--fork). Daemon forks if true.
bool m_daemon;
//! Debug mode flag (--debug).
bool m_debugMode;
/*! Flag indicating boot mode (--boot-mode). If true, then:
* - Caches won't be initialized.
* - Booster respwan delay is 0.
@ -228,6 +237,13 @@ private:
typedef map<int, sighandler_t> SigHandlerMap;
SigHandlerMap m_originalSigHandlers;
//! True if re-execing
bool m_reExec;
//! Name of the state saving directory and file
static const char *m_stateDir;
static const char *m_stateFile;
#ifdef UNIT_TEST
friend class Ut_Daemon;
#endif

@ -38,6 +38,7 @@ char g_pipeDataSigTerm = SIGTERM;
char g_pipeDataSigUsr1 = SIGUSR1;
char g_pipeDataSigUsr2 = SIGUSR2;
char g_pipeDataSigPipe = SIGPIPE;
char g_pipeDataSigHup = SIGHUP;
static void sigChldHandler(int)
{
@ -64,6 +65,11 @@ static void sigPipeHandler(int)
write(g_sigPipeFd, &g_pipeDataSigPipe, 1);
}
static void sigHupHandler(int)
{
write(g_sigPipeFd, &g_pipeDataSigHup, 1);
}
//! Main function
DECL_EXPORT int main(int argc, char * argv[])
{
@ -100,6 +106,7 @@ DECL_EXPORT int main(int argc, char * argv[])
myDaemon.setUnixSignalHandler(SIGUSR1, sigUsr1Handler); // enter normal mode from boot mode
myDaemon.setUnixSignalHandler(SIGUSR2, sigUsr2Handler); // enter boot mode (same as --boot-mode)
myDaemon.setUnixSignalHandler(SIGPIPE, sigPipeHandler); // broken invoker's pipe
myDaemon.setUnixSignalHandler(SIGHUP, sigHupHandler); // re-exec
// Run the main loop
myDaemon.run();

@ -133,3 +133,12 @@ unsigned int SocketManager::socketCount() const
return m_socketHash.size();
}
SocketManager::SocketHash SocketManager::getState()
{
return m_socketHash;
}
void SocketManager::addMapping(const string & socketId, int fd)
{
m_socketHash[socketId] = fd;
}

@ -60,9 +60,17 @@ public:
//! Return count of currently active sockets
unsigned int socketCount() const;
// Type of the internal state
typedef map<string, int> SocketHash;
//! Get the state
SocketHash getState();
//! Add mapping of socketId to fd
void addMapping(const string & socketId, int fd);
private:
typedef map<string, int> SocketHash;
SocketHash m_socketHash;
};

@ -7,4 +7,3 @@
# available, parameters will be cut off. The main function will still always
# get the complete argument list.
exec /usr/bin/applauncherd.bin $@ " "

@ -0,0 +1,9 @@
#!/bin/sh
# Use 50 chars long dummy parameter to reserve place for application
# name and parameters so that we get some space to modify the argument
# list seen in /proc/[PID]/cmdline.
# If the length of the application name with parameters is longer than space
# available, parameters will be cut off. The main function will still always
# get the complete argument list.
exec /usr/bin/applauncherd.bin --debug $@ " "
Loading…
Cancel
Save