Fixes: NB#212024 - Application launcher should be usable (improve performance) also at boot

RevBy: Antti Kervinen

Details:
--boot-mode starts applauncherd in the boot mode:
* Boosters won't initialize cache
* Booster respawn delay is zero

Sending SIGUSR1 forces the launcher to enter the normal mode again.
pull/1/head
Jussi Lind 15 years ago
parent 5af417187a
commit 0ff715fb64

@ -3,7 +3,9 @@ project(Applauncherd)
cmake_minimum_required(VERSION 2.6)
cmake_policy(VERSION 2.6)
set(CMAKE_VERBOSE_MAKEFILE ON)
#
# NOTE: For verbose build use VERBOSE=1
#
# Default C++-flags. Sub-builds might alter these.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wextra -g -O3 -Wl,--as-needed")
@ -78,6 +80,8 @@ add_definitions(-DBOOSTER_PLUGIN_DIR="/usr/lib/applauncherd")
add_definitions(-DSINGLE_INSTANCE_PATH="/usr/bin/single-instance")
# Disable debug logging, only error and warning messages get logged
# Currently effective only for invoker. Launcher part recognizes --debug
# which enables console echoing and debug messages.
add_definitions(-DDEBUG_LOGGING_DISABLED)
# Build with test coverage switch if BUILD_COVERAGE environment variable is set

@ -49,7 +49,7 @@
// Delay before exit
static const unsigned int DEFAULT_DELAY = 0;
static const unsigned int RESPAWN_DELAY = 2;
static const unsigned int RESPAWN_DELAY = 3;
static const unsigned int MAX_RESPAWN_DELAY = 10;
static const unsigned char EXIT_STATUS_APPLICATION_CONNECTION_LOST = 0xfa;

@ -52,7 +52,8 @@ Booster::Booster() :
m_connection(NULL),
m_oldPriority(0),
m_oldPriorityOk(false),
m_spaceAvailable(0)
m_spaceAvailable(0),
m_bootMode(false)
{}
Booster::~Booster()
@ -64,20 +65,19 @@ Booster::~Booster()
m_appData = NULL;
}
bool Booster::preload()
{
return true;
}
void Booster::initialize(int initialArgc, char ** initialArgv, int newPipeFd[2],
int socketFd, SingleInstance * singleInstance)
int socketFd, SingleInstance * singleInstance,
bool newBootMode)
{
m_bootMode = newBootMode;
setPipeFd(newPipeFd);
// Drop priority (nice = 10)
pushPriority(10);
// Preload stuff
if (!m_bootMode)
preload();
// Rename process to temporary booster process name, e.g. "booster-m"
@ -139,6 +139,11 @@ void Booster::initialize(int initialArgc, char ** initialArgv, int newPipeFd[2],
prctl(PR_SET_DUMPABLE, 1);
}
bool Booster::bootMode() const
{
return m_bootMode;
}
void Booster::sendDataToParent()
{
// Signal the parent process that it can create a new

@ -68,15 +68,16 @@ public:
/*!
* \brief Initializes the booster process.
* \param initialArgc argc of the parent process.
* \param initialArgv argv of the parent process.
* \param pipefd pipe used to communicate with the parent process.
* \param socketFd File socket used to get commands from the invoker.
* \param singleInstance Pointer to a valid SingleInstance object.
* \param bootMode Booster-specific preloads are not executed if true.
*/
virtual void initialize(int initialArgc, char ** initialArgv, int pipefd[2],
int socketFd, SingleInstance * singleInstance);
/*!
* \brief Preload libraries.
* Override in the custom Booster.
*/
virtual bool preload();
int socketFd, SingleInstance * singleInstance,
bool bootMode);
/*!
* \brief Run the application to be invoked.
@ -141,17 +142,21 @@ public:
* \brief Return the communication socket used by a Booster.
* This method returns the socket used between invoker and the Booster.
* (common to all Boosters of the type). Override in the custom Booster.
* \return Path to the socket file
* \return Path to the socket file.
*/
virtual const string & socketId() const = 0;
protected:
//! Return true, if in boot mode.
bool bootMode() const;
//! Set nice value and store the old priority. Return true on success.
bool pushPriority(int nice);
protected:
//! Restore the old priority stored by the previous successful setPriority().
bool popPriority();
/*!
* \brief Preload libraries / initialize cache etc.
* Called from initialize if not in the boot mode.
* Re-implement in the custom Booster.
*/
virtual bool preload() = 0;
/*!
* \brief Wait for connection from invoker and read the input.
@ -163,6 +168,12 @@ protected:
*/
virtual bool receiveDataFromInvoker(int socketFd);
//! Set nice value and store the old priority. Return true on success.
bool pushPriority(int nice);
//! Restore the old priority stored by the previous successful setPriority().
bool popPriority();
//! Sets pipe fd's used to communicate with the parent process
void setPipeFd(int pipeFd[2]);
@ -209,6 +220,9 @@ private:
//! Original space available for arguments
int m_spaceAvailable;
//! True, if being run in boot mode.
bool m_bootMode;
#ifdef HAVE_CREDS
//! filter out invoker-specific credentials from boosted application
static void filterOutCreds(creds_t creds);

@ -33,7 +33,6 @@
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <signal.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <glob.h>
@ -46,6 +45,7 @@ const int Daemon::m_boosterSleepTime = 2;
Daemon::Daemon(int & argc, char * argv[]) :
m_daemon(false),
m_quiet(false),
m_bootMode(false),
m_socketManager(new SocketManager),
m_singleInstance(new SingleInstance)
{
@ -69,9 +69,24 @@ Daemon::Daemon(int & argc, char * argv[]) :
m_initialArgv = argv;
m_initialArgc = argc;
if (pipe(m_pipefd) == -1)
if (pipe(m_boosterPipeFd) == -1)
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe failed!\n");
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe for boosters failed!\n");
}
if (pipe(m_sigChldPipeFd) == -1)
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe for SIGCHLD failed!\n");
}
if (pipe(m_sigTermPipeFd) == -1)
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe for SIGTERM failed!\n");
}
if (pipe(m_sigUsr1PipeFd) == -1)
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe for SIGUSR1 failed!\n");
}
// Daemonize if desired
@ -181,16 +196,78 @@ void Daemon::run()
// Main loop
while (true)
{
// Wait for something appearing in the pipe. There should first
// appear a character representing the type of a booster and then
// pid of the invoker process that communicated with that booster.
// Variables used by the select call
fd_set rfds;
int ndfs = 0;
// Init data for select
FD_ZERO(&rfds);
FD_SET(m_boosterPipeFd[0], &rfds);
ndfs = std::max(ndfs, m_boosterPipeFd[0]);
FD_SET(m_sigChldPipeFd[0], &rfds);
ndfs = std::max(ndfs, m_sigChldPipeFd[0]);
FD_SET(m_sigTermPipeFd[0], &rfds);
ndfs = std::max(ndfs, m_sigTermPipeFd[0]);
FD_SET(m_sigUsr1PipeFd[0], &rfds);
ndfs = std::max(ndfs, m_sigUsr1PipeFd[0]);
char buffer;
// Wait for something appearing in the pipes.
if (select(ndfs + 1, &rfds, NULL, NULL, NULL) > 0)
{
Logger::logDebug("select done.");
// Check if a booster died
if (FD_ISSET(m_boosterPipeFd[0], &rfds))
{
Logger::logDebug("FD_ISSET(m_boosterPipeFd[0])");
readFromBoosterPipe(m_boosterPipeFd[0]);
}
// Check if we got SIGCHLD
if (FD_ISSET(m_sigChldPipeFd[0], &rfds))
{
Logger::logDebug("FD_ISSET(m_sigChldPipeFd[0])");
read(m_sigChldPipeFd[0], &buffer, 1);
reapZombies();
}
// Check if we got SIGTERM
if (FD_ISSET(m_sigTermPipeFd[0], &rfds))
{
Logger::logDebug("FD_ISSET(m_sigTermPipeFd[0])");
read(m_sigTermPipeFd[0], &buffer, 1);
exit(EXIT_SUCCESS);
}
// Check if we got SIGUSR1
if (FD_ISSET(m_sigUsr1PipeFd[0], &rfds))
{
Logger::logDebug("FD_ISSET(m_sigUsr1PipeFd[0])");
read(m_sigUsr1PipeFd[0], &buffer, 1);
enterNormalMode();
}
}
}
}
void Daemon::readFromBoosterPipe(int fd)
{
// There should first appear a character representing the type of a
// booster and then pid of the invoker process that communicated with
// that booster.
char msg;
ssize_t count = read(m_pipefd[0], static_cast<void *>(&msg), 1);
ssize_t count = read(fd, static_cast<void *>(&msg), 1);
if (count)
{
// Read pid of peer invoker
pid_t invokerPid;
count = read(m_pipefd[0], static_cast<void *>(&invokerPid), sizeof(pid_t));
count = read(fd, static_cast<void *>(&invokerPid), sizeof(pid_t));
if (count < static_cast<ssize_t>(sizeof(pid_t)))
{
@ -213,7 +290,7 @@ void Daemon::run()
// Read booster respawn delay
int delay;
count = read(m_pipefd[0], static_cast<void *>(&delay), sizeof(int));
count = read(fd, static_cast<void *>(&delay), sizeof(int));
if (count < static_cast<ssize_t>(sizeof(int)))
{
@ -221,7 +298,7 @@ void Daemon::run()
}
else
{
Logger::logDebug("Daemon: respawn delay: %d \n", delay);
Logger::logDebug("Daemon: respawn delay received: %d \n", delay);
}
// Fork a new booster of the given type
@ -236,16 +313,17 @@ void Daemon::run()
{
Logger::logWarning("Daemon: Nothing read from the pipe\n");
}
}
}
void Daemon::killProcess(pid_t pid) const
void Daemon::killProcess(pid_t pid, int signal) const
{
if (pid)
{
if (kill(pid, SIGKILL) != 0)
Logger::logDebug("Daemon: Killing pid %d with %d", pid, signal);
if (kill(pid, signal) != 0)
{
Logger::logError("Daemon: Failed to kill %d: %s\n", pid, strerror(errno));
Logger::logError("Daemon: Failed to kill %d: %s\n",
pid, strerror(errno));
}
}
}
@ -274,7 +352,7 @@ void Daemon::loadSingleInstancePlugin()
void Daemon::loadBoosterPlugins()
{
const char* PATTERN = "lib*booster.so";
const int BUF_LEN = 256;
const unsigned int BUF_LEN = 256;
if (strlen(PATTERN) + strlen(BOOSTER_PLUGIN_DIR) + 2 > BUF_LEN)
{
@ -322,6 +400,9 @@ void Daemon::loadBoosterPlugins()
void Daemon::forkBooster(char type, int sleepTime)
{
// Invalidate current booster pid for the given type
setPidToBooster(type, 0);
// Fork a new process
pid_t newPid = fork();
@ -333,12 +414,25 @@ void Daemon::forkBooster(char type, int sleepTime)
// Reset used signal handlers
signal(SIGCHLD, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGUSR1, SIG_DFL);
// Will get this signal if applauncherd dies
prctl(PR_SET_PDEATHSIG, SIGHUP);
// Close unused read end
close(m_pipefd[0]);
// Close unused read end of the booster pipe
close(m_boosterPipeFd[0]);
// Close SIGCHLD pipe
close(m_sigChldPipeFd[0]);
close(m_sigChldPipeFd[1]);
// Close SIGTERM pipe
close(m_sigTermPipeFd[0]);
close(m_sigTermPipeFd[1]);
// Close SIGUSR1 pipe
close(m_sigUsr1PipeFd[0]);
close(m_sigUsr1PipeFd[1]);
// Close unused sockets inherited from daemon
closeUnusedSockets(type);
@ -352,7 +446,8 @@ void Daemon::forkBooster(char type, int sleepTime)
// Guarantee some time for the just launched application to
// start up before forking new booster if needed.
if (sleepTime)
// Not done if in the boot mode.
if (!m_bootMode && sleepTime)
sleep(sleepTime);
Logger::logDebug("Daemon: Running a new Booster of type '%c'", type);
@ -362,9 +457,9 @@ void Daemon::forkBooster(char type, int sleepTime)
if (booster)
{
// Initialize and wait for commands from invoker
booster->initialize(m_initialArgc, m_initialArgv, m_pipefd,
booster->initialize(m_initialArgc, m_initialArgv, m_boosterPipeFd,
m_socketManager->findSocket(booster->socketId().c_str()),
m_singleInstance);
m_singleInstance, m_bootMode);
// Run the current Booster
booster->run(m_socketManager);
@ -566,17 +661,67 @@ void Daemon::parseArgs(const ArgVect & args)
{
for (ArgVect::const_iterator i(args.begin()); i != args.end(); i++)
{
if ((*i) == "--daemon")
if ((*i) == "--daemon" || (*i) == "-d")
{
m_daemon = true;
}
else if ((*i) == "--quiet")
else if ((*i) == "--debug")
{
Logger::setDebugMode(true);
}
else if ((*i) == "--quiet" || (*i) == "-q")
{
m_quiet = true;
}
else if ((*i) == "--boot-mode" || (*i) == "-b")
{
Logger::logInfo("Daemon: Boot mode set.");
m_bootMode = true;
}
}
}
int Daemon::sigChldPipeFd() const
{
return m_sigChldPipeFd[1];
}
int Daemon::sigTermPipeFd() const
{
return m_sigTermPipeFd[1];
}
int Daemon::sigUsr1PipeFd() const
{
return m_sigUsr1PipeFd[1];
}
void Daemon::enterNormalMode()
{
if (m_bootMode)
{
m_bootMode = false;
// Kill current boosters
killBoosters();
Logger::logInfo("Daemon: Exited boot mode.");
}
}
void Daemon::killBoosters()
{
TypeMap::iterator iter(m_boosterTypeToPid.begin());
while (iter != m_boosterTypeToPid.end())
{
killProcess(iter->second, SIGTERM);
iter++;
}
// NOTE!!: m_boosterTypeToPid must not be cleared
// in order to automatically start new boosters.
}
Daemon::~Daemon()
{
delete m_socketManager;

@ -37,6 +37,8 @@ using std::vector;
using std::map;
#include <signal.h>
class Booster;
class SocketManager;
class SingleInstance;
@ -88,6 +90,15 @@ public:
//! Unlock file (lock is not needed in boosters)
static void unlock();
//! Get fd to write when SIGCHLD arrives
int sigChldPipeFd() const;
//! Get fd to write when SIGTERM arrives
int sigTermPipeFd() const;
//! Get fd to write when SIGUSR1 arrives
int sigUsr1PipeFd() const;
private:
//! Disable copy-constructor
@ -118,8 +129,8 @@ private:
//! Don't use console for output
void consoleQuiet();
//! Kill given pid with SIGKILL
void killProcess(pid_t pid) const;
//! Kill given pid with SIGKILL by default
void killProcess(pid_t pid, int signal = SIGKILL) const;
//! Load (dlopen()) booster plugins
void loadBoosterPlugins();
@ -139,12 +150,29 @@ private:
//! Close all sockets NOT used by the given booster type.
void closeUnusedSockets(char type);
//! Daemonize flag
//! Read and process data from a booster pipe
void readFromBoosterPipe(int fd);
//! Enter normal mode (restart boosters with cache enabled)
void enterNormalMode();
//! Kill all active boosters with -9
void killBoosters();
//! Daemonize flag (--fork). Daemon forks if true.
bool m_daemon;
//! Debug print flag
//! Debug print flag (--quiet). Daemon closes fd's 0 - 2 if true.
bool m_quiet;
/*! Flag indicating boot mode (--boot-mode). If true, then:
* - Caches won't be initialized.
* - Booster respwan delay is 0.
*
* Normal mode is activated by firing SIGUSR1.
*/
bool m_bootMode;
//! Vector of current child PID's
typedef vector<pid_t> PidVect;
PidVect m_children;
@ -157,8 +185,18 @@ private:
typedef map<char, pid_t> TypeMap;
TypeMap m_boosterTypeToPid;
//! Pipe used to tell the parent that a new booster is needed
int m_pipefd[2];
//! Pipe used to tell the parent that a new booster is needed +
//! some parameters.
int m_boosterPipeFd[2];
//! Pipe used to safely catch SIGCHLD
int m_sigChldPipeFd[2];
//! Pipe used to safely catch SIGTERM
int m_sigTermPipeFd[2];
//! Pipe used to safely catch SIGUSR1
int m_sigUsr1PipeFd[2];
//! Argument vector initially given to the launcher process
int m_initialArgc;

@ -26,7 +26,7 @@
bool Logger::m_isOpened = false;
bool Logger::m_echoMode = false;
bool Logger::m_debugMode = false;
void Logger::openLog(const char * progName)
{
@ -48,8 +48,8 @@ void Logger::writeLog(const int priority, const char * format, va_list ap)
{
if (Logger::m_isOpened)
{
// In echo mode everything is printed also to stdout
if (m_echoMode)
// In debug mode everything is printed also to stdout
if (m_debugMode)
{
vprintf(format, ap);
printf("\n");
@ -62,14 +62,13 @@ void Logger::writeLog(const int priority, const char * format, va_list ap)
void Logger::logDebug(const char * format, ...)
{
#ifndef DEBUG_LOGGING_DISABLED
if (m_debugMode)
{
va_list(ap);
va_start(ap, format);
writeLog(LOG_DEBUG, format, ap);
va_end(ap);
#else
(void)format;
#endif
}
}
void Logger::logInfo(const char * format, ...)
@ -107,8 +106,8 @@ void Logger::logErrorAndDie(int code, const char * format, ...)
_exit(code);
}
void Logger::setEchoMode(bool enable)
void Logger::setDebugMode(bool enable)
{
Logger::m_echoMode = enable;
Logger::m_debugMode = enable;
}

@ -42,9 +42,9 @@ public:
*/
static void closeLog();
/*!
* \brief Log a notice to the system message logger
* \brief Log a debug to the system message logger.
* Effective only if Logger::setDebugMode(true) called;
* \param format String identical to a printf format string
* \param additionalArgs Depending on the format string, the function may expect a
* sequence of additional arguments, each containing one value to be inserted
@ -52,7 +52,6 @@ public:
*/
static void logDebug(const char * format, ...);
/*!
* \brief Log an error to the system message logger
* \param format String identical to a printf format string
@ -62,7 +61,6 @@ public:
*/
static void logError(const char * format, ...);
/*!
* \brief Log a warning to the system message logger
* \param format String identical to a printf format string
@ -72,7 +70,6 @@ public:
*/
static void logWarning(const char * format, ...);
/*!
* \brief Log a piece of information to the system message logger
* \param format String identical to a printf format string
@ -91,19 +88,20 @@ public:
*/
static void logErrorAndDie(int code, const char * format, ...);
/*!
* \brief Forces Logger to echo everything to stdout if set to true.
* \brief Forces Logger to log everything and echo to stdout if set to true.
*/
static void setEchoMode(bool enable);
static void setDebugMode(bool enable);
private:
static void writeLog(const int priority, const char * format, va_list ap);
//! True if the log is open
static bool m_isOpened;
//! Echo everything to stdout if true
static bool m_echoMode;
//! Echo everything including debug messages to stdout if true
static bool m_debugMode;
};
#endif // LOGGER_H

@ -28,24 +28,30 @@
#define DECL_EXPORT extern "C" __attribute__ ((__visibility__("default")))
//! Signal handler to reap zombie processes
void reapZombies(int)
int g_sigChldPipeFd = -1;
int g_sigTermPipeFd = -1;
int g_sigUsr1PipeFd = -1;
char g_dummyPipeData = 0;
static void sigChldHandler(int)
{
if (Daemon::instance())
{
Daemon::instance()->reapZombies();
}
write(g_sigChldPipeFd, &g_dummyPipeData, 1);
}
static void sigTermHandler(int)
{
write(g_sigTermPipeFd, &g_dummyPipeData, 1);
}
void exitLauncher(int)
static void sigUsr1Handler(int)
{
exit(0);
write(g_sigUsr1PipeFd, &g_dummyPipeData, 1);
}
//! Main function
DECL_EXPORT int main(int argc, char * argv[])
{
// Open the log
Logger::openLog(PROG_NAME_LAUNCHER);
Logger::logDebug("%s starting..", PROG_NAME_LAUNCHER);
@ -56,13 +62,19 @@ DECL_EXPORT int main(int argc, char * argv[])
Logger::logErrorAndDie(EXIT_FAILURE, "%s is already running \n", PROG_NAME_LAUNCHER);
}
// Install signal handlers
signal(SIGCHLD, reapZombies);
signal(SIGTERM, exitLauncher);
// Create main daemon instance
Daemon myDaemon(argc, argv);
// Get fd's for signal pipes.
g_sigChldPipeFd = myDaemon.sigChldPipeFd();
g_sigTermPipeFd = myDaemon.sigTermPipeFd();
g_sigUsr1PipeFd = myDaemon.sigUsr1PipeFd();
// Install signal handlers
signal(SIGCHLD, sigChldHandler); // reap zombies
signal(SIGTERM, sigTermHandler); // exit launcher
signal(SIGUSR1, sigUsr1Handler); // restore normal mode
// Run the main loop
myDaemon.run();
@ -71,4 +83,3 @@ DECL_EXPORT int main(int argc, char * argv[])
return EXIT_SUCCESS;
}

@ -41,6 +41,7 @@ bool MBooster::preload()
// an MApplication)
MComponentCache::populateForMApplication();
#endif
return true;
}
@ -66,6 +67,15 @@ char MBooster::type()
bool MBooster::receiveDataFromInvoker(int socketFd)
{
// Use the default implementation if in boot mode
// (it won't require QApplication running).
if (bootMode())
{
return Booster::receiveDataFromInvoker(socketFd);
}
else
{
// Setup the conversation channel with the invoker.
setConnection(new Connection(socketFd));
@ -87,5 +97,6 @@ bool MBooster::receiveDataFromInvoker(int socketFd)
}
return true;
}
}

@ -47,9 +47,6 @@ public:
//! \brief Destructor
virtual ~MBooster() {}
//! \reimp
virtual bool preload();
/*!
* \brief Return the socket name common to all MBooster objects.
* \return Path to the socket file.
@ -77,6 +74,9 @@ public:
protected:
//! \reimp
virtual bool preload();
//! \reimp
virtual bool receiveDataFromInvoker(int socketFd);

@ -58,11 +58,11 @@ public:
*/
static char type();
protected:
//! \reimp
virtual bool preload();
protected:
//! \reimp
virtual const string & socketId() const;

@ -6,5 +6,5 @@
# 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 " "
exec /usr/bin/applauncherd.bin $@ " "

@ -30,6 +30,9 @@ public:
const std::string & socketId() const;
const std::string & boosterTemporaryProcessName() const;
protected:
bool preload();
private:
const string m_socketId;
const string m_temporaryProcessName;
@ -45,6 +48,11 @@ char MyBooster::boosterType() const
return 'x';
}
bool MyBooster::preload()
{
return true;
}
const std::string & MyBooster::boosterTemporaryProcessName() const
{
return m_temporaryProcessName;

Loading…
Cancel
Save