Merge pull request #34 from rburchell/master

Clean up some remnants of the single-process-applauncherd model.
pull/1/head
Robin Burchell 10 years ago
commit 45ed31bffd

@ -2,8 +2,8 @@ What is applauncherd?
==============================
Applauncherd is a daemon that helps to launch applications faster by
preloading dynamically linked libraries and caching stuff (MComponentCache). It also saves memory, because all launched
applications share certain resources.
preloading dynamically linked libraries and caching stuff.
It also saves memory, because all launched applications share certain resources.
Applauncherd also provides support for fast single instance launches.
@ -23,17 +23,15 @@ Building
Technical overview
==============================
The applauncherd daemon is started by UpStart as part of XSession, that
is, at the same level as the desktop (MeeGo Touch homescreen).
Applauncherd forks the will-be-application process, "booster", before
knowing which application is going to be launched next. There can be
Booster daemons (written using the provided library) are started as part of the
user session. The booster is responsible for forking the will-be-application
before knowing which application is going to be launched next. There can be
different kinds of boosters optimized for different kinds of
applications, e.g. Qt or QML.
In the current architecture boosters are loaded as plugins. Applauncherd
searches for plugin libraries in /usr/lib/applaucherd/lib*booster.so and
forks a new process for each booster to wait for launch commands from the
user.
In the current architecture boosters are implemented as seperate processes,
using the provided support library. Each booster process waits for launch
commands.
The user uses the launcher always through a special invoker program. The
invoker (/usr/bin/invoker) tells booster process to load an application
@ -47,22 +45,13 @@ In that case exec() is used.
Technical details
==============================
Before loading, booster process changes its security credentials so that
the code in the application binary will be executed with the correct credentials. Loading the binary is done with dlopen(), and therefore the
Loading the binary is done with dlopen(), and therefore the
application needs to be compiled and linked as a shared library or a
position independent executable. The booster process also sets the
environment variables. Finally, it finds the main function in the
application binary with dlsym() and calls the main() with the command
line arguments given by the invoker.
The launcher itself is a library that is loaded by a small C-program (/usr/bin/applauncherd.bin). Idea behind this is to avoid linking the
launcher binary to any libraries. This allows us to fully control how
(with which flags) the preloaded libraries are opened with dlopen().
Each application type (currently Qt or QtDeclarative) has its own booster
process. When booster launches the application by calling the "main()"
function, applauncherd will create new booster process of that type.
Booster processes do some initializations that cannot be shared among
other processes and therefore have to be done after forking. This allows,
for instance, instantiating a application before knowing the
@ -73,6 +62,12 @@ launched.
Contributors
==============================
People who have contributed to mapplauncherd:
Robin Burchell
John Brooks
Thomas Perl
People who have contributed to meegotouch-applauncherd:
Olli Leppänen

@ -96,7 +96,6 @@ Daemon::Daemon(int & argc, char * argv[]) :
m_boosterPid(0),
m_socketManager(new SocketManager),
m_singleInstance(new SingleInstance),
m_reExec(false),
m_notifySystemd(false),
m_booster(0)
{
@ -125,21 +124,16 @@ 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 (!m_reExec && socketpair(AF_UNIX, SOCK_DGRAM, 0, m_boosterLauncherSocket) == -1)
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, m_boosterLauncherSocket) == -1)
{
throw std::runtime_error("Daemon: Creating a socket pair for boosters failed!\n");
}
if (!m_reExec && pipe(m_sigPipeFd) == -1)
if (pipe(m_sigPipeFd) == -1)
{
throw std::runtime_error("Daemon: Creating a pipe for Unix signals failed!\n");
}
@ -167,14 +161,6 @@ void Daemon::run(Booster *booster)
// dlopen single-instance
loadSingleInstancePlugin();
if (m_reExec)
{
// Reap dead booster processes and restart them
// Note: this cannot be done before booster plugins have been loaded
reapZombies();
}
else
{
// Create socket for the booster
Logger::logDebug("Daemon: initing socket: %s", booster->boosterType().c_str());
m_socketManager->initSocket(booster->boosterType());
@ -182,7 +168,6 @@ void Daemon::run(Booster *booster)
// Fork each booster for the first time
Logger::logDebug("Daemon: forking booster: %s", booster->boosterType().c_str());
forkBooster();
}
// Notify systemd that init is done
if (m_notifySystemd) {
@ -251,14 +236,6 @@ void Daemon::run(Booster *booster)
Logger::logDebug("Daemon: SIGPIPE received.");
break;
case SIGHUP:
Logger::logDebug("Daemon: SIGHUP received.");
reExec();
// not reached if re-exec successful
break;
default:
break;
}
@ -600,10 +577,6 @@ void Daemon::parseArgs(const ArgVect & args)
{
usage(args[0].c_str(), EXIT_SUCCESS);
}
else if ((*i) == "--re-exec")
{
m_reExec = true;
}
else if ((*i) == "--systemd")
{
m_notifySystemd = true;
@ -727,257 +700,3 @@ Daemon::~Daemon()
Logger::closeLog();
}
void Daemon::reExec()
{
Logger::logInfo("Daemon: Re-exec requested.");
struct stat st;
if (stat(m_stateDir.c_str(), &st) != 0)
{
Logger::logDebug("Daemon: State saving directory %s does not exist", m_stateDir.c_str());
Logger::logDebug("Daemon: Attempting to create it");
if (mkdir(m_stateDir.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0)
{
Logger::logDebug("Daemon: Failed to create directory, re-exec failed, exiting.");
_exit(1);
}
}
if (stat(m_stateDir.c_str(), &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.c_str());
_exit(1);
}
try {
std::ofstream ss(m_stateFile.c_str());
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;
}
ss << "booster-pid " << m_boosterPid << 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;
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[] = { const_cast<char*>("/usr/bin/applauncherd.bin"),
const_cast<char*>("--re-exec"),
const_cast<char*>(" "),
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()
{
try
{
// We have saved state, try to restore it.
std::ifstream ss(m_stateFile.c_str());
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.c_str()) == -1)
{
Logger::logError("Daemon: could not remove state file %s", m_stateFile.c_str());
}
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-pid")
{
int arg1;
ss >> arg1;
Logger::logDebug("Daemon: restored m_boosterPid = %d", arg1);
m_boosterPid = arg1;
}
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 == "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.c_str()) == -1)
{
Logger::logError("Daemon: could not remove state file %s", m_stateFile.c_str());
}
// This is only reached if state restore was unsuccessful.
_exit(1);
}

@ -144,12 +144,6 @@ private:
//! Prints the usage and exits with given status
void usage(const char *name, int status);
//! Re-exec applauncherd.bin
void reExec();
//! Restore state.
void restoreState();
//! Daemonize flag (--fork). Daemon forks if true.
bool m_daemon;
@ -208,9 +202,6 @@ private:
typedef map<int, sighandler_t> SigHandlerMap;
SigHandlerMap m_originalSigHandlers;
//! True if re-execing
bool m_reExec;
//! True if systemd needs to be notified
bool m_notifySystemd;

Loading…
Cancel
Save