Changes: Pipe between applauncherd and boosters replaced with socket pair.

RevBy: Antti Kervinen

Details: Booster passes invoker socket connection to applauncherd
and after that applaucherd takes care of boosted application
exit value and passes it to invoker. Exit value works for booster-e.
pull/1/head
Juha Lintula 15 years ago
parent f5b1339650
commit 53a410c266

@ -40,6 +40,7 @@
#include <X11/Xutil.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <grp.h>
#ifdef HAVE_CREDS
#include <sys/creds.h>
@ -95,13 +96,13 @@ Booster::~Booster()
m_appData = NULL;
}
void Booster::initialize(int initialArgc, char ** initialArgv, int newPipeFd[2],
void Booster::initialize(int initialArgc, char ** initialArgv, int newBoosterLauncherSocket,
int socketFd, SingleInstance * singleInstance,
bool newBootMode)
{
m_bootMode = newBootMode;
setPipeFd(newPipeFd);
setBoosterLauncherSocket(newBoosterLauncherSocket);
// Drop priority (nice = 10)
pushPriority(10);
@ -156,11 +157,13 @@ void Booster::initialize(int initialArgc, char ** initialArgv, int newPipeFd[2],
renameProcess(initialArgc, initialArgv, m_appData->argc(), m_appData->argv());
// Send parent process a message that it can create a new booster,
// send pid of invoker, send booster respawn value
// send pid of invoker, booster respawn value and invoker socket connection
sendDataToParent();
// close pipe
close(pipeFd(1));
close(boosterLauncherSocket());
// close invoker socket connection
m_connection->close();
// Don't care about fate of parent applauncherd process any more
prctl(PR_SET_PDEATHSIG, 0);
@ -173,23 +176,52 @@ bool Booster::bootMode() const
void Booster::sendDataToParent()
{
struct iovec iov[3];
struct msghdr msg;
struct cmsghdr *cmsg;
char buf[CMSG_SPACE(sizeof(int))];
// Signal the parent process that it can create a new
// waiting booster process and close write end
const char msg = boosterType();
if (write(pipeFd(1), static_cast<const void *>(&msg), 1) == -1) {
Logger::logError("Booster: Couldn't send type message to launcher process\n");
}
const char booster = boosterType();
iov[0].iov_base = const_cast<char *>(&booster);
iov[0].iov_len = sizeof(char);
// Send to the parent process pid of invoker for tracking
pid_t pid = invokersPid();
if (write(pipeFd(1), static_cast<const void *>(&pid), sizeof(pid_t)) == -1) {
Logger::logError("Booster: Couldn't send invoker's pid to launcher process\n");
}
iov[1].iov_base = &pid;
iov[1].iov_len = sizeof(pid_t);
// Send to the parent process booster respawn delay value
int delay = m_appData->delay();
if (write(pipeFd(1), static_cast<const void *>(&delay), sizeof(int)) == -1) {
Logger::logError("Booster: Couldn't send respawn delay value to launcher process\n");
iov[2].iov_base = &delay;
iov[2].iov_len = sizeof(int);
msg.msg_iov = iov;
msg.msg_iovlen = 3;
msg.msg_name = NULL;
msg.msg_namelen = 0;
if (m_connection->isReportAppExitStatusNeeded())
{
// Send socket file descriptor to parent
int fd = m_connection->getFd();
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
}
else
{
msg.msg_control = NULL;
msg.msg_controllen = 0;
}
if (sendmsg(boosterLauncherSocket(), &msg, 0) < 0)
{
Logger::logError("Booster: Couldn't send data to launcher process\n");
}
}
@ -221,38 +253,25 @@ bool Booster::receiveDataFromInvoker(int socketFd)
return false;
}
void Booster::run(SocketManager * socketManager)
int Booster::run(SocketManager * socketManager)
{
if (!m_appData->fileName().empty())
{
// Check if can close sockets already here
if (!m_connection->isReportAppExitStatusNeeded())
// We can close sockets here because
// socket FD is passed to daemon already
if (socketManager)
{
if (socketManager)
{
socketManager->closeAllSockets();
}
socketManager->closeAllSockets();
}
// Execute the binary
Logger::logDebug("Booster: invoking '%s' ", m_appData->fileName().c_str());
int ret_val = launchProcess();
// Send exit status to invoker, if needed
if (m_connection->isReportAppExitStatusNeeded())
{
m_connection->sendAppExitStatus(ret_val);
m_connection->close();
if (socketManager)
{
socketManager->closeAllSockets();
}
}
return launchProcess();
}
else
{
Logger::logError("Booster: nothing to invoke\n");
return EXIT_FAILURE;
}
}
@ -553,15 +572,14 @@ pid_t Booster::invokersPid()
}
}
void Booster::setPipeFd(int newPipeFd[2])
void Booster::setBoosterLauncherSocket(int newBoosterLauncherSocket)
{
m_pipeFd[0] = newPipeFd[0];
m_pipeFd[1] = newPipeFd[1];
m_boosterLauncherSocket = newBoosterLauncherSocket;
}
int Booster::pipeFd(bool whichEnd) const
int Booster::boosterLauncherSocket() const
{
return m_pipeFd[whichEnd];
return m_boosterLauncherSocket;
}
Connection* Booster::connection() const

@ -70,12 +70,12 @@ 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 boosterLauncherSocket socket connection to the parent process.
* \param socketFd 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],
virtual void initialize(int initialArgc, char ** initialArgv, int boosterLauncherSocket,
int socketFd, SingleInstance * singleInstance,
bool bootMode);
@ -85,12 +85,13 @@ public:
* using dlopen(). Program execution jumps to the address of
* "main()" found in the newly loaded library. The Booster process
* exits with corresponding exit-code after the execution of
* main() has finished.
* main() has finished. run() returns the return value of the main()
* function.
*
* \param socketManager Pointer to the SocketManager so that
* we can close all needless sockets in the application process.
*/
virtual void run(SocketManager * socketManager);
virtual int run(SocketManager * socketManager);
/*!
* \brief Rename process.
@ -200,11 +201,11 @@ protected:
//! 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]);
//! Sets socket fd's used to communicate with the parent process
void setBoosterLauncherSocket(int boosterLauncherSocket);
//! Returns the given pipe fd (0 = read end, 1 = write end)
int pipeFd(bool whichEnd) const;
//! Returns the given socket
int boosterLauncherSocket() const;
//! Reset out-of-memory killer adjustment
void resetOomAdj();
@ -237,8 +238,8 @@ private:
//! it can be restored later.
bool m_oldPriorityOk;
//! Pipe used to tell the parent that a new booster is needed
int m_pipeFd[2];
//! Socket used to tell the parent that a new booster is needed
int m_boosterLauncherSocket;
//! Original space available for arguments
int m_spaceAvailable;

@ -66,6 +66,11 @@ Connection::Connection(int socketFd, bool testMode) :
#endif
}
int Connection::getFd() const
{
return m_fd;
}
bool Connection::accept(AppData* appData)
{
if (!m_testMode)
@ -234,14 +239,6 @@ bool Connection::sendPid(pid_t pid)
return true;
}
bool Connection::sendAppExitStatus(int status)
{
sendMsg(INVOKER_MSG_EXIT);
sendMsg(status);
return true;
}
uint32_t Connection::receiveMagic()
{
uint32_t magic = 0;

@ -67,15 +67,15 @@ public:
//! \brief Check connection state.
bool connected() const;
//! \brief Get invoker socket file descriptor
int getFd() const;
//! \brief Receive application data to appData.
bool receiveApplicationData(AppData* appData);
//! \brief Return true if invoker wait for process exit status
bool isReportAppExitStatusNeeded() const;
//! \brief Send application exit status to invoker
bool sendAppExitStatus(int status);
//! \brief Get pid of the process on the other end of socket connection
pid_t peerPid();

@ -28,7 +28,6 @@
#include <cstdlib>
#include <cerrno>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@ -70,9 +69,9 @@ Daemon::Daemon(int & argc, char * argv[]) :
m_initialArgv = argv;
m_initialArgc = argc;
if (pipe(m_boosterPipeFd) == -1)
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, m_boosterLauncherSocket) == -1)
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a pipe for boosters failed!\n");
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: Creating a socket pair for boosters failed!\n");
}
if (pipe(m_sigPipeFd) == -1)
@ -189,8 +188,8 @@ void Daemon::run()
// Init data for select
FD_ZERO(&rfds);
FD_SET(m_boosterPipeFd[0], &rfds);
ndfs = std::max(ndfs, m_boosterPipeFd[0]);
FD_SET(m_boosterLauncherSocket[0], &rfds);
ndfs = std::max(ndfs, m_boosterLauncherSocket[0]);
FD_SET(m_sigPipeFd[0], &rfds);
ndfs = std::max(ndfs, m_sigPipeFd[0]);
@ -201,10 +200,10 @@ void Daemon::run()
Logger::logDebug("select done.");
// Check if a booster died
if (FD_ISSET(m_boosterPipeFd[0], &rfds))
if (FD_ISSET(m_boosterLauncherSocket[0], &rfds))
{
Logger::logDebug("FD_ISSET(m_boosterPipeFd[0])");
readFromBoosterPipe(m_boosterPipeFd[0]);
Logger::logDebug("FD_ISSET(m_boosterLauncherSocket[0])");
readFromBoosterSocket(m_boosterLauncherSocket[0]);
}
// Check if we got SIGCHLD, SIGTERM, SIGUSR1 or SIGUSR2
@ -244,63 +243,62 @@ void Daemon::run()
}
}
void Daemon::readFromBoosterPipe(int fd)
void Daemon::readFromBoosterSocket(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(fd, static_cast<void *>(&msg), 1);
if (count)
{
// Read pid of peer invoker
pid_t invokerPid;
count = read(fd, static_cast<void *>(&invokerPid), sizeof(pid_t));
if (count < static_cast<ssize_t>(sizeof(pid_t)))
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: pipe connection with booster failed");
}
else
{
Logger::logDebug("Daemon: invoker's pid: %d \n", invokerPid);
}
char booster = 0;
pid_t invokerPid = 0;
int delay = 0;
struct msghdr msg;
struct cmsghdr *cmsg;
struct iovec iov[3];
char buf[CMSG_SPACE(sizeof(int))];
iov[0].iov_base = &booster;
iov[0].iov_len = sizeof(char);
iov[1].iov_base = &invokerPid;
iov[1].iov_len = sizeof(pid_t);
iov[2].iov_base = &delay;
iov[2].iov_len = sizeof(int);
msg.msg_iov = iov;
msg.msg_iovlen = 3;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
if (recvmsg(fd, &msg, 0) >= 0)
{
Logger::logDebug("Daemon: booster type: %c\n", booster);
Logger::logDebug("Daemon: invoker's pid: %d\n", invokerPid);
Logger::logDebug("Daemon: respawn delay: %d \n", delay);
if (invokerPid != 0)
{
// Store booster - invoker pid pair
pid_t boosterPid = boosterPidForType(msg);
// Store booster - invoker socket pair
pid_t boosterPid = boosterPidForType(booster);
if (boosterPid)
{
cmsg = CMSG_FIRSTHDR(&msg);
int newFd = *reinterpret_cast<int *>(CMSG_DATA(cmsg));
Logger::logDebug("Daemon: socket file descriptor: %d\n", newFd);
m_boosterPidToInvokerPid[boosterPid] = invokerPid;
m_boosterPidToInvokerFd[boosterPid] = newFd;
}
}
// Read booster respawn delay
int delay;
count = read(fd, static_cast<void *>(&delay), sizeof(int));
if (count < static_cast<ssize_t>(sizeof(int)))
{
Logger::logErrorAndDie(EXIT_FAILURE, "Daemon: pipe connection with booster failed");
}
else
{
Logger::logDebug("Daemon: respawn delay received: %d \n", delay);
}
// Fork a new booster of the given type
// 2nd param guarantees some time for the just launched application
// to start up before forking new booster. Not doing this would
// slow down the start-up significantly on single core CPUs.
forkBooster(msg, delay);
}
else
{
Logger::logWarning("Daemon: Nothing read from the pipe\n");
}
// Fork a new booster of the given type
// 2nd param guarantees some time for the just launched application
// to start up before forking new booster. Not doing this would
// slow down the start-up significantly on single core CPUs.
forkBooster(booster, delay);
}
void Daemon::killProcess(pid_t pid, int signal) const
@ -407,8 +405,8 @@ void Daemon::forkBooster(char type, int sleepTime)
// Will get this signal if applauncherd dies
prctl(PR_SET_PDEATHSIG, SIGHUP);
// Close unused read end of the booster pipe
close(m_boosterPipeFd[0]);
// Close unused read end of the booster socket
close(m_boosterLauncherSocket[0]);
// Close signal pipe
close(m_sigPipeFd[0]);
@ -420,6 +418,16 @@ void Daemon::forkBooster(char type, int sleepTime)
// Close lock file, it's not needed in the booster
Daemon::unlock();
// Close socket file descriptors
FdMap::iterator i(m_boosterPidToInvokerFd.begin());
while (i != m_boosterPidToInvokerFd.end())
{
if ((*i).second != -1) {
close((*i).second);
(*i).second = -1;
}
i++;
}
// Set session id
if (setsid() < 0)
Logger::logError("Daemon: Couldn't set session id\n");
@ -437,19 +445,19 @@ void Daemon::forkBooster(char type, int sleepTime)
if (booster)
{
// Initialize and wait for commands from invoker
booster->initialize(m_initialArgc, m_initialArgv, m_boosterPipeFd,
booster->initialize(m_initialArgc, m_initialArgv, m_boosterLauncherSocket[1],
m_socketManager->findSocket(booster->socketId().c_str()),
m_singleInstance, m_bootMode);
// Run the current Booster
booster->run(m_socketManager);
int retval = booster->run(m_socketManager);
// Finish
delete booster;
// _exit() instead of exit() to avoid situation when destructors
// for static objects may be run incorrectly
_exit(EXIT_SUCCESS);
_exit(retval);
}
else
{
@ -489,7 +497,21 @@ void Daemon::reapZombies()
{
Logger::logDebug("Daemon: Terminated process had a mapping to an invoker pid");
if (WIFSIGNALED(status))
if (WIFEXITED(status))
{
Logger::logDebug("child exited by exit(x), _exit(x) or return x\n");
Logger::logDebug("x == %d\n", WEXITSTATUS(status));
FdMap::iterator fd = m_boosterPidToInvokerFd.find(pid);
if (fd != m_boosterPidToInvokerFd.end())
{
write((*fd).second, &INVOKER_MSG_EXIT, sizeof(uint32_t));
int exitStatus = WEXITSTATUS(status);
write((*fd).second, &exitStatus, sizeof(int));
close((*fd).second);
m_boosterPidToInvokerFd.erase(fd);
}
}
else if (WIFSIGNALED(status))
{
int signal = WTERMSIG(status);
pid_t invokerPid = (*it).second;

@ -38,6 +38,7 @@ using std::vector;
using std::map;
#include <signal.h>
#include <sys/socket.h>
class Booster;
class SocketManager;
@ -148,7 +149,7 @@ private:
void closeUnusedSockets(char type);
//! Read and process data from a booster pipe
void readFromBoosterPipe(int fd);
void readFromBoosterSocket(int fd);
//! Enter normal mode (restart boosters with cache enabled)
void enterNormalMode();
@ -184,13 +185,17 @@ private:
typedef map<pid_t, pid_t> PidMap;
PidMap m_boosterPidToInvokerPid;
//! Storage of booster <-> invoker socket file descriptor pairs
typedef map<pid_t, pid_t> FdMap;
FdMap m_boosterPidToInvokerFd;
//! Mapping for booster type <-> pid
typedef map<char, pid_t> TypeMap;
TypeMap m_boosterTypeToPid;
//! Pipe used to tell the parent that a new booster is needed +
//! Socket pair used to tell the parent that a new booster is needed +
//! some parameters.
int m_boosterPipeFd[2];
int m_boosterLauncherSocket[2];
//! Pipe used to safely catch Unix signals
int m_sigPipeFd[2];

@ -40,6 +40,5 @@ M_EXPORT int main(int argc, char ** argv)
std::cerr << "uid=" << usr_id <<"\n";
std::cerr << "gid=" << grp_id <<"\n";
app->exec();
return 29;
_exit(29);
}

@ -448,13 +448,20 @@ class launcher_tests (unittest.TestCase):
app_st_wo_inv = os.WEXITSTATUS(st)
debug("The exit status of app without invoker is : %d" %app_st_wo_inv)
#Run application with invoker and get the exit status
#Run application with invoker and get the exit status - booster-m case
debug("Run application with invoker and get the exit status")
st, op = commands.getstatusoutput('invoker --type=m --wait-term /usr/bin/fala_status')
app_st_w_inv = os.WEXITSTATUS(st)
debug("The exit status of app with invoker is : %d" %app_st_w_inv)
debug("The exit status of app with invoker (booster-m) is : %d" %app_st_w_inv)
self.assert_(app_st_wo_inv == app_st_w_inv, "The invoker returns a wrong exit status")
#Run application with invoker and get the exit status - booster-e case
debug("Run application with invoker and get the exit status")
st, op = commands.getstatusoutput('invoker --type=e --wait-term /usr/bin/fala_status')
app_st_we_inv = os.WEXITSTATUS(st)
debug("The exit status of app with invoker (booster-e) is : %d" %app_st_we_inv)
self.assert_(app_st_wo_inv == app_st_w_inv, "The invoker returns a wrong exit status for booster-m")
self.assert_(app_st_wo_inv == app_st_we_inv, "The invoker returns a wrong exit status for booster-e")
def test_invoker_gid_uid(self):
"""

Loading…
Cancel
Save